I am trying to find the most concise (and meaningful) way of using Java Optional, to read the first value off a Optional<String> and return the String if exists, or return "NOT_FOUND". Here is the code I am working with:
public static String getValue(Optional<String> input) {
return input.ifPresent(val -> val.get()).orElse("NOT_FOUND")
}
The methods of Optional apparently have very specific purposes but the API has left me confused.
Update (4/13/2018):
The code in my question is incorrect, because if I regarded val as the value inside the Optional, then val.get() does not make any sense. Thanks for pointing that out, #rgettman.
Also, I added another part to my question in the accepted answer's comments, i.e. I needed a way to manipulate the String value, if present, before returning. The orElse("NOT_FOUND") is still applicable, if the Optional does not contain a value. So what is an acceptable use of the Optional API to achieve the following?
public static String getValue(Optional<String> input) {
return input.isPresent() ? input.get().substring(0,7).toUpperCase() : "NOT_FOUND";
}
#Aominè's answer and follow up comments addressed both parts of this question.
All you have to do is change your return statement to:
return input.orElse("NOT_FOUND");
This will return the object in the Optional if present else returns "NOT_FOUND".
That said, I'd avoid using Optional's as parameters. see here.
If you need to manipulate the string value, if it is present before returning it, use map method:
public static String getValue(Optional<String> input) {
return input.map(s -> s.substring(0,7).toUpperCase()).orElse("NOT_FOUND");
}
If input is empty the method returns default value - "NOT_FOUND", otherwise capitalized part of the string is returned.
getValue(Optional.ofNullable(null));
$6 ==> "NOT_FOUND"
getValue(Optional.of("some long string"));
$7 ==> "SOME LO"
Related
This is a code segment from another StackOverflow question:
#Override
public String convertToDatabaseColumn(final UUID entityValue) {
return ofNullable(entityValue).map(entityUuid -> entityUuid.toString()).orElse(null);
}
I am really struggling to understand the use of the Optional class. Is the return code saying "return the value of the map (a String) or NULL if that fails?
How can return be acting on a method rather than a Class - that is Optional.ofNullable()?
This is a really bad use of Optional. In fact the java developers themself say that optional should not be used in such cases, but only as a return argument from a method. More can be read in this great answer: Is it good practice to use optional as an attribute in a class
The code can be rewritten to this:
#Override
public String convertToDatabaseColumn(final UUID entityValue) {
return entityValue == null ? null : entityValue.toString();
}
Is the return code saying "return the value of the map (a String) or NULL if that fails?
Yes. You can check the documentation of Optional here. It tells you exactly what map and orElse do.
How can return be acting on a method rather than a Class - that is Optional.ofNullable()?
You are not returning the method. You are returning the return value of a method. Look at this simple example:
int myMethod() {
return foo();
}
int foo() { return 10; }
See? I am not returning foo the method, I am returning 10, the return value of foo.
Note that it is possible to return methods, with functional interfaces.
In this case, you are returning the return value of the last method in the method chain, orElse. ofNullable creates an Optional<T>, then map is called on this object and returns a new Optional<T>, then orElse is called and its return value is returned.
Lets go step by step:
ofNullable(entityValue)
creates an Optional of the incoming parameter (which is allowed to be null, using of() a NPE gets thrown for null input)
.map(entityUuid -> entityUuid.toString())
Then you pick the actual value, and invoke toString() on that value ... which only happens if entityValue isn't null. If it is null, the result comes from orElse(null).
In the end, the result of that operation on the Optional is returned as result of the method.
The above code is nothing but a glorified version of
if (entityValue == null) return null;
return entityValue.toString();
Optionals have their place in Java, but your example isn't a good one.
It doesn't help readability a bit, and you are not alone with wondering "what is going on here".
The code can be turn like this :
public String convertToDatabaseColumn(final UUID entityValue) {
if(entityValue==null){
return null;
}else{
return entityValue.toString();
}
}
Your initial code have two statements:
Optional.ofNullable(entityValue): create an Optional Object to say the value can be present or not.
.map(entityUuid -> entityUuid.toString()).orElse(null); you apply some operation to your Optional object, return a string of it or null.
This will avoid a null pointer exception in a more elegant way.
Optional.ofNullable(T value):
Returns an Optional describing the specified value, if non-null, otherwise returns an empty Optional.
Optional.orElse(null)
Return the value if present, otherwise return null.
Follow this link
I have a snippet as below
private String getString() {
List<String> stringList = Arrays.asList("s1", "s2");
stringList.forEach(item ->
{
if (item.equals("s1"))
return item;
});
return null;
}
Now I get a compilation error Void methods cannot return a value while I try to return item. I googled and couldn't understand why this is happening and the solution to this problem. How do I return item in the above forEach loop?
forEach is a consumption process. The functions passed to a forEach method are expected to have side effects which do not directly result in a value being returned, like printing the contents of the list, or doing some side operation.
Basically, any function that is a Consumer or method that returns void is fair game here.
Since you're explicitly looking for a value, you can use Stream#findAny() instead.
// You don't do anything with the result you want anyway,
// so I'm simply printing it out. You should determine what you need to do
// with the value you get back.
Optional<String> findResult = stringList().stream().findAny(v -> v.equals("s1"));
findResult.ifPresent(System.out::println);
I think you've misunderstood lambdas. A lambda is an executable statement that may be executed immediately, or deferred; more importantly, it has its own method signature.
In the following code, you've defined an anonymous method. What is its signature?
stringList.forEach(item ->
{
if (item.equals("s1"))
return item;
});
The answer is that your anonymous method has the following equivalent representation:
private void myAnonymousMethod( String item ) {
if ( item.equals("s1"))
return item;
}
Do you now see your mistake? Your anonymous method has a void return type, but you're trying to return a value.
Instead, you could fix this in a variety of ways:
When you find the value you're looking for, save it in a local variable outside the scope of the lambda. Pro: introduces the smallest amount of change to your code. Con: the foreach loop will continue to execute after it finds its first match.
Use a different iterator method, such as findAny as indicated in Makoto's post.
Replace the anonymous method entirely with a normal loop.
I'm following this tutorial for creating reminder in android. In the source code that it provides it used the value of "1" for a boolean method.
Here is the code snippet I'm talking about:
public static boolean showRemainingTime(){
return "1".equals(sp.getString(TIME_OPTION, "0"));
}
Why "1" is used in this example given that in java the value of boolean is either true or false?
Sorry for my lame question!
The showRemainingTime method is not returning the String "1". It returns true if the String returned by sp.getString(TIME_OPTION, "0") is equal to the String "1", and false otherwise.
String literals are full-fledged String objects. This may make more sense to you:
String str = "1";
return str.equals(sp.getString(TIME_OPTION, "0"));
It might also make more sense if it were written this way:
return sp.getString(TIME_OPTION, "0").equals("1");
The problem with this version is that if getString(...) returned null, calling equals(...) would throw a NullPointerException. That may not be possible in this particular case, but calling methods on string literals is a good habit to get into.
I started to learn Lambda expressions of Java 8, and wrote below program to get sum of all numbers in the list:
import java.util.Arrays;
import java.util.List;
public class MainClass {
public static void main(String[] args) {
List<Integer> number = Arrays.asList(1, 2, 3, 4, 5);
System.out.println(number.stream().reduce((c,e) -> {
return c + e;
}));
}
}
I was expecting the output to be:
15
but I got:
Optional[15]
Java version: 1.8.0_45
Please explain what does Optional[] means in the output?
Does it has any significance in Java 8?
From the Java Docs for Stream#reduce(), we see that the reduce operation returns an Optional<T>. An Optional simply wraps a value if there is a value present, otherwise is "empty".
Important operations on Optional include Optional#isPresent, which lets you know if there is something in the Optional or not, Optional#get, which returns the T wrapped in the Optional and throws an exception if called on Empty, and Optional#orElse which returns the T wrapped in the Optional if present, or the returns the default value provided if called on Empty.
For your case, the rationale behind reduce() returning an Optional<Integer> is that the list you're trying to reduce may be empty. In that case, the Integer that should be returned is not well defined. For your specific intention 0 would be acceptable (As the sum of the elements in an empty list is 0), thus you can get the sum as follows:
int sum = number.stream().reduce((c,e) -> c + e).orElse(0);
That way, even if the list is empty, you will still get a result that defines the sum of the list.
reduce(BinaryOperator<T> accumulator):
Returns an Optional describing the result of the reduction
Optional:
A container object which may or may not contain a non-null value. If a value is present, isPresent() will return true and get() will return the value.
The reason reduce() returns an Optional, is because the stream might be empty, in which case there would be nothing to reduce, aka no value, and an Optional.empty() would be returned.
In order to avoid Optional in the return you can call to this other method https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html#reduce-T-java.util.function.BinaryOperator- Just add the identity: "0" before adding the lambda expression. Note that now there is only a 0 as a fist parameter to the reduce call.
System.out.println(number.stream().reduce(0,(c,e) -> {
return c + e;
}));
returns just
15
Got it, thanks #Mshnik and #TimoSta. According to source code of Optional<> which overrides toString method
#Override
public String toString() {
return value != null
? String.format("Optional[%s]", value)
: "Optional.empty";
}
Above code adds that Optional[] in my output stream.
From java.lang.Object.Optional:
A container object which may or may not contain a non-null value. If a value is present, isPresent() will return true and get() will return the value.
Optional offers two different primary methods for retrieving its value.
public T get() simply returns the value enclosed in the Optional, but throws a NoSuchElementException if the Optional does not wrap a value.
orElse(T other) returns the value enclosed in the Optional or other if the Optional does not enclose a value.
EDIT (thanks #Brian Goetz):
Generally, orElse() is a better choice since get() returns a NoSuchElementException if the Optional contains a null value. True, in this case you will always recieve a value in Optional but it's still good practice to primarily use orElse().
So, in your case, you would change
System.out.println(number.stream().reduce((c,e) -> {
return c + e;
}));
to:
System.out.println(number.stream().reduce((c,e) -> {
return c + e;
}).orElse(0));
which will return the desired value of 15.
And as #Brian Goetz said in the comments, if you really wanted to make it succinct you could use Integer::sum and a method reference:
System.out.println(number.stream.reduce(Integer::sum).orElse(0))
which is equivalent to using the longer lambda.
This is my source code. I'trying to implement a simple program that asks a question to a user and expects the answer to be "yes" or "no" and terminates only if the user answer to the question "yes" or "no". The book I have suggested me not to use == comparison and to use the equals method instead, so that the program can understand if the user typed "y e s" instead of "yes". But in this way the result is the same and the method seems to compare the user's answer if it is exactly "yes" or "no". It doesn't accept for example an aswer of "n o". Is that logical for that method? Is it supposed to work that way? How can I change the program to accept answers like "Yes" "ye s" "No" "NO" etc.? I would appreciate your help:)
import acm.program.*;
public class YesNoExample extends ConsoleProgram{
public void run(){
while(true){
String answer = readLine("Would you like instructions? ");
if(askYesNoQuestion(answer)){
break;
}
println("Please answer yes or no.");
}
}
private boolean askYesNoQuestion(String str){
if(str.equals("yes")||str.equals("no")){
return true;
}else{
return false;
}
}
}
If you use == you'll be comparing the references (memory pointers) of two String objects. If you use equals, a custom made method in the String class will be run that does some "intelligent" comparison, in this case, check that the characters are all the same, and the whole thing has the same length.
If you'd like to support mixed case letters, you could use "someString".equalsIgnoreCase("SoMeString") (which will return true). This is done (said roughly) by making both strings lowercase (so the case doesn't matter) and comparing them using equals.
Edit: The other posters made me realize that, in addition to capitalization, you also want to look for String equality where spaces don't matter. If that's the case, a similar trick to turning everything to lowercase applies, where you first remove all the spaces, as #LouisWasserman says in his answer
If you need to fuzzily identify yes/no, first you need exact rules as to what matches. Based on your examples, I can suggest this:
private boolean askYesNoQuestion(String str) {
str = str.replace(" ", "").toUpperCase();
return str.equals("YES") || str.equals("NO");
}
If interested in top performance and not at all in intelligibility, use this:
private static final Pattern p =
Pattern.compile("y\\s*e\\s*s|n\\s*o", Pattern.CASE_INSENSITIVE);
private boolean askYesNoQuestion(String str) {
return p != null && p.matcher(str.trim()).matches();
}
Semantics of == vs .equals()
First off you misunderstand the semantics.
== tests for object identity. A == B says is A a reference to the exact same object as B.
.equals() applies custom logic to test if the objects are equal in some logical manner, without being the exact same object. For this to be implemented correct, both objects should have the same .hashCode() value as well.
Idiomatic Java Solution
Since the String object is final which means it can't be inherited from. You can't override the .equals() on the String object.
What you need to do is preprocess the input into something that can be directly compared to the target value with .equalsIgnoreCase().
One way to do this is use, answer.replaceAll("\\s","") to remove all the whitespace then you can compare it to your target String literal with .equalsIgnoreCase().
A better method to replace askYesNoQuestion() would be:
private boolean isAnswerYesOrNo(final String answer)
{
final String input = answer.replaceAll("\\s","");
return "yes".equalsIgnoreCase(input) || "no".equalsIgnoreCase(input);
}
Comparing a literal to the input parameter will insulate you from NullPointerExceptions if the input parameter happens to be null "yes".equalsIgnoreCase()can never throw aNullPointerException`. This is idiomatic Java.
Get a better book
That book isn't very useful if it really says what you are claiming it says. Also it is teaching you to write lots of code to handle bad input when that is a complete anti-pattern and a well designed program would exit with a verbose explanation of the exact problem was what can be done to fix the input.
With the explanation of == and .equals well described above, here's a two examples of a one liner that does the comparison you want.
if ( Pattern.matches("\\s*[yY]\\s*[eE]\\s*[sS]\\s*", input) ) {
// do something
}
if ( input.replaceAll("\\s", "").equalsIgnoreCase("yes") ) {
// do something
}