Valid usage of Optional type in Java 8
Asked Answered
S

4

11

Is this a valid (intended) usage of Optional type in Java 8?

String process(String s) {
    return Optional.ofNullable(s).orElseGet(this::getDefault);
}
Saudra answered 24/12, 2015 at 5:40 Comment(5)
@ŁukaszRzeszotarski has some point here. Have a look at the javax.money API - they are using the same construct: github.com/JavaMoney/jsr354-ri/blob/master/src/main/java/org/…Aggrieve
@PetarTahchiev yes after the discussion I came to the conclusion that the whole concept of Optional in jdk is really sad. While designers thoughts, what he or they meant is not enough to use the new api correctly. If it is only for some reasons good it should be either limited in the public Api or at least well documented. I believe many misuse the concept because of these reasons.Glabrate
@PetarTahchiev, String.valueOf(amount) is a much shorter alternative to do the same which exists since JDK 1.0.Inflict
The JavaMoney example mentioned above has moved. It does appear to be an egregious use of Optional but that seems rare in core Java. There is one example in Locale.java but again, this usage seems rare.Minny
@Minny Seems legit if you have nested null checks, isn‘t it?Glabrate
Z
28

I'll take another swing at this.

Is this a valid usage? Yes, in the narrow sense that it compiles and produces the results that you're expecting.

Is this intended usage? No. Now, sometimes things find usefulness beyond what they were originally for, and if this works out, great. But for Optional, we have found that usually things don't work out very well.

Brian Goetz and I discussed some of the issues with Optional in our JavaOne 2015 talk, API Design With Java 8 Lambdas and Streams:

The primary use of Optional is as follows: (slide 36)

Optional is intended to provide a limited mechanism for library method return types where there is a clear need to represent "no result," and where using null for that is overwhelmingly likely to cause errors.

The ability to chain methods from an Optional is undoubtedly very cool, and in some cases it reduces the clutter from conditional logic. But quite often this doesn't work out. A typical code smell is, instead of the code using method chaining to handle an Optional returned from some method, it creates an Optional from something that's nullable, in order to chain methods and avoid conditionals. Here's an example of that in action (also from our presentation, slide 42):

// BAD
String process(String s) {
    return Optional.ofNullable(s).orElseGet(this::getDefault);
}

// GOOD
String process(String s) {
    return (s != null) ? s : getDefault();
}

The method that uses Optional is longer, and most people find it more obscure than the conventional code. Not only that, it creates extra garbage for no good reason.

Bottom line: just because you can do something doesn't mean that you should do it.

Zimmerman answered 24/12, 2015 at 22:53 Comment(2)
isn't it legit though if you have many nested null checks?Glabrate
@ŁukaszRzeszotarski Wow, following up on your own question after nearly 8 years? :-) If you have lots of optional-returning methods, chaining them with flatMap seems sensible. To me the odd thing is creating an Optional for the specific purpose of handling an actual null. Seems like it's trying to replace "safe navigation" or "safe call" operators (like ?. in Kotlin) with Optional, which doesn't work very well. Finally, long chains of nullable method calls it might indicate a modeling issue. Look up "Law of Demeter". But that leads off into a different discussion.Zimmerman
B
7

Since this is more or less an opinion-based question, I'll throw mine in. If you're trying to say

if (id == 1) {
    Foo f = new Foo(id, "Bar", "US");
    return "Bar".equals(f.getName()) && "US".equals(f.getCountryCode());
} else {
    return false;
}

then just say that. Making things "functional" doesn't automatically make things clearer or better. By introducing a needless Optional, a couple lambdas, and some Optional methods that I had to look up, you've made the code more convoluted and difficult to understand. I don't think the designers of Java "intended" for people to use Optional to help make code more obscure.

EDIT: After reading some responses, I think it's worth adding some comments. This is not a functional programming idiom I'm familiar with, which would make it harder to understand. The idioms I am familiar with mostly involve Java streams, or (in other languages) functional idioms applied to multiple values in arrays or lists or other collections of multiple values. In those cases, once you get past the unfamiliarity, the functional syntax can be seen as an improvement because it allows some details to be hidden (loop indexes, iterators, running pointers, accumulator variables). So overall, it can simplify things. This example, by itself, doesn't do any such simplification.

However, some of the Optional features are useful in stream contexts. Suppose we had a parseInt() method that returns an Optional<Integer>, which is empty if the input string is invalid. (Java 8 really should have provided this.) This would make it easy to take an array of strings and produce an array of integers in which the strings that don't parse are simply eliminated from the result--use parseInt in a stream map(), and use a stream filter to filter out the empty Optionals. (I've seen multiple StackOverflow questions asking how to do this.) If you want to keep only the positive values, you could use an Optional.filter() to change the nonpositives to Optional.empty() before using the stream filter (although in this case, you could add another stream filter afterwards, but in a more complex case the Optional filter could be more useful). That's what I see as the main benefit of Optional from a functional standpoint. It allows you to work with a collection of values all at once, by giving you a way to represent "non-values" and write a function that will still work with them. So I guess the main use of Optional, besides a replacement for null, would be to represent empty spaces in a sequence of values while you're applying functions to the entire sequence as a whole.

Besse answered 24/12, 2015 at 7:47 Comment(2)
Hmmm in principle I don't agree. You think it is easier to read because you are accustomed to some syntax. For me readability for both is the same. But agree with yhsavit that when designers made it for another purposes it shouldn't be used like that.Glabrate
One punkt more is ... as I said I agree with yshavit that when intention is to use it only for public methods that possible may return null it should be use like that. But another issue is a filter syntax, which you don't like. Ir then why it is in public Optional api? For me conclusion is that Optional type has a very limited usage in Java 8, and actually is not as great feature as many think.Glabrate
S
4

Asking whether it's "valid" is rather opinion-based, but as to whether it's the intended use case: no, it's not.

Brian Goetz, Oracle's language architect for Java, has stated that the use case for Optional is for when you need a "no value" marker, and when using null for this is likely to cause errors. Specifically, if a reasonable user of your method is not likely to consider the possibility that its result is null, then you should use Optional. It was explicitly not intended to be a general "Maybe"-type object, as you're using it here.

In your case, the method that returns the Optional is private. That means it can only be used by the implementers of the class, and you can assume that they have good knowledge of the class' methods — including which of them may return null. Since there's no reasonable risk of confusion, Brian Goetz would (probably) say that he would not consider this a valid use case.

Scene answered 24/12, 2015 at 5:55 Comment(6)
Would be your answer 'Yes' if it was public method?Glabrate
@ŁukaszRzeszotarski The answer would then be "it depends." Specifically, on whether you think that just returning null is "overwhelmingly likely to cause errors" (quote taken from Goetz's answer that I linked to in my answer). That would be a judgement call we can't make from this snippet of code.Scene
I consider this statement a bit confusing 'Brian Goetz, Oracle's language architect for Java, has stated that the use case for Optional is for when you need a "no value" marker, and when using null for this is likely to cause errors. ' Would be great if the api would be so designed/ documented that its usage would be clear without interprating stackoverflow statements of Mr. Brian Goetz. But as I understood you your opinion is that it makes sense only for public methods, which are supposed to return null values?!Glabrate
Yup, that's about the gist of it. As for what might have been great or not, that's opinion-based and off topic for SO. I tried to keep to the objective facts, which seem to indicate that by the designers' original intention, private methods should never return Optional (and it's a judgement call as to which public methods should return it).Scene
Your last paragraph is based entirely on assumptions. According to you it's off topic.Also
@zeroflagL: There's pure opinion, and there's judgement call. To make the judgment call that the implementer of a class should know the methods of that class is not off topic. It's like the difference between "what's the best flavor of ice cream?" and "is eating two pints of ice cream every day healthy?"Scene
S
2

Its a little contrived, but 'valid' (as in 'syntactically') , but as @yshavit pointed to, it was intended for use in library development.


Previous answer was due to FP code being difficult to read. Below is commented(a little verbose, b/c that is the javadoc comments) but still. Much easier to read IMHO. (2nd is no-comments, and at least alignment to help readability)

private boolean isFooValid(final Integer id) {
    return getFoo(id)
        // filter if 'f' matches the predicate, return Optional w/f if true, empty Optional if false
        .filter(f -> "Bar".equals(f.getName()) && "US".equals(f.getCountryCode())) 
        // If a value is present, apply the provided mapping function to it, 
        // If non-null, return an Optional describing the result.  
        .map(f -> true)
        // Return the value if present, otherwise return other.
        .orElse(false);
 }

Or at least line it up so its more apparent what is going on and easier to read.

private boolean isFooValid(final Integer id) {
    return getFoo(id)
        .filter(f -> "Bar".equals(f.getName()) && "US".equals(f.getCountryCode())) 
        .map(f -> true)
        .orElse(false);
 }
Submit answered 24/12, 2015 at 5:55 Comment(2)
Wrong, I think, because if getFoo(int) returns an empty Optional, filter will not call getName(), which is part of a lambda.Besse
Updated after you pointed out 'filter'. Didn't even see the word when i looked the first time :/Submit

© 2022 - 2024 — McMap. All rights reserved.