What do you think is better (with arguments, of course):
Optional.ofNullable( userName )
.ifPresent( nonNullUserName -> header.setUser( createUser( nonNullUserName ) ) );
or
header.setUser( userName == null ? createUser( userName ) : null );
The method createUser creates xml element and the intent of the whole peace of code is to set it in a SOAP request depending on presence of userName.
The benefits of the first approach I see is the absence of useless operations, the code does one thing and no more. But the second approach lets you save one more line of code thus appearing to be more laconic.
UPDATE: I guess I missed a thing I actually implied and it caused certain misunderstanding. It would be better to provide cleaner examples with explanations if you have some.
Optional was designed to be used as a return type for methods that can't always return a value, like for example getFirstElementOfAList(), which can't return anything else if the list is empty: returning null would be less clear than returning an empty Optional, and could mean that the first element is null, and not that there is no first element.
The designers don't intend Optional to be used as a convenience construct to chain methods. See the presentation by Stuart Marks (who works on the JDK), and especially what he says at the 28th minute:
It's generally a bad idea to create an Optional for the specific purpose of chaining methods [...]
You're of course free to disrespect the intended design and the best practices, but your colleagues might also be free to hate you for that :-)
Note that the two snippets you posted don't do the same thing. The first one does nothing if the user name is null, whereas the second one sets the user to null (which might in turn throw an exception or overwrite the previous value)
I would just use (assuming what you want is the first version):
if (userName != null) {
header.setUser(createUser(userName));
}
which is extremely clear, doesn't abuse Optional, and doesn't create an Optional instance just to chain methods.
They are different things, one is an Object, another is an operator. you shouldn't comparing between them. the first approach can be simplified to this, which will be more readable & descriablable:
Optional.ofNullable(userName).map(this::createUser).ifPresent(header::setUser);
IF you really want to comparing between them, the only one different is you have been mentioned above. and if you process condition or result more complex in an operator that will result to the expression is too complex to read.
Related
I found implemented this code (method where this part of code is used is returning Optional<File>, this part of code had to be added for verification if values are correct and case can be saved):
if (!CaseChecker.checkValues(case)) {
return Optional.of(new File("FALSE"));
}
When I asked the person, why he is returning something like this. The answer was "Optional can contain any value". To be honest I don't agree with this, because type of Optional is for some reason. So I wanted to confirm if Optional can be really anything and if yes, then what's the reason to write Optional type.
The whole point of Optional is to avoid the situation where you return a bogus value (like null, or like returning -1 from String.indexOf in order to indicate it didn't find anything), putting the responsibility on the caller to know what is bogus and to know to check for it. Returning Optional lets the caller know it needs to check whether a valid result came back, and it takes away the burden of the caller code needing to know what return values aren't valid.
Optional was never meant as a wrapper for a bogus value.
And yes you can return anything. The api doesn't stop you from doing nonsensical things.
Also consider that flatMap doesn't know what to do with this weird thing, it will treat the contents as nonempty. So now how you can use this value is limited. You have a multistage validation process where it might be something you can handle with operations chained together using flatMap, but this fake file has made that harder. It's limited your options.
Return Optional.empty() if you don't have a valid value to return.
Question ahead:
why does in Java the call coll.contains(null) fail for ImmutableCollections?
I know, that immutable collections cannot contain null-elements, and I do not want to discuss whether that's good or bad.
But when I write a Function, that takes a (general, not explicit immutable) Collection, it fails upon checking for nulls. Why does the implementation not return false (which is actually the 'correct' answer)?
And how can I properly check for nulls in a Collection in general?
Edit:
with some discussions (thanks to the commenters!) I realized, that I mixed up two things: ImmutableCollection from the guava library, and the List returned by java.util.List.of, being some class from ImmutableCollections. However, both classes throw an NPE on .contains(null).
My problem was with the List.of result, but technically the same would happen with guaves implementation. [edit: It does not]
I am distressed by this discussion!
Collections that do this have been a pet peeve of mine since before I wrote the first collections that eventually became Guava. If you find any Guava collection that throws NPE just because you asked it a perfectly innocent question like .contains(null), please file a bug! We hate that crap.
EDIT: I was so distressed that I had to go back to look at my 2007 changelist that first created ImmutableSet and saw literally this:
#Override public boolean contains(#Nullable Object target) {
if (target == null) {
return false;
}
ahhhhh.
why does in Java the call coll.contains(null) fail for ImmutableCollections?
Because the design team (the ones who have created guava) decided that, for their collections, null is unwanted, and therefore any interaction between their collections and a null check, even in this case, should just throw to highlight to the programmer, at the earliest possible opportunity, that there is a mismatch. Even where the established behaviour (as per the existing implementations in the core runtime itself, such as ArrayList and friends, as well as the javadoc), rather explicitly go the other way and say that a non-sequitur check (is this pear part of this list of apples?) strongly suggests that the right move is to just return false and not throw.
In other words, guava messed up. But now that they have done so, going back is potentially backwards compatibility breaking. It really isn't very - you are replacing an exception thrown with a false return value; presumably code could be out there that relies on the NPE (catching it and doing something different from what the code would do had contains(null) returned false instead of throwing) - but that's a rare case, and guava breaks backwards compatibility all the time.
And how can I properly check for nulls in a Collection in general?
By calling .contains(null), just as you are. The fact that guava doesn't do it right doesn't change the answer. You might as well ask 'how do I add elements to a list', and counter the answer of "well, you call list.add(item) to do that" with: Well, I have this implementation of the List interface that plays Rick Astley over the speaker instead of adding to the list, so, I reject your answer.
That's.. how java and interfaces work: You can have implementations of them, and the only guardianship that they do what the interface dictates they must, is that the author understands there is a contract that needs to be followed.
Now, normally a library so badly written they break contract for no good reason*, isn't popular. But guava IS popular. Very popular. That gets at a simple truth: No library is perfect. Guava's API design is generally quite good (in my opinion, vastly superior to e.g. Apache commons libraries), and the team actively spends a lot of time debating proper API design, in the sense that the code that one would write using guava is nice (as defined by: Easy to understand, has few surprises, easy to maintain, easy to test, and probably easy to mutate to deal with changing requirements - the only useful definition for nebulous terms like 'nice' or 'elegant' code - it's code that does those things, anything else is pointless aesthetic drivel). In other words, they are actively trying, and they usually get it right.
Just, not in this case. Work around it: return item != null && coll.contains(item); will get the job done.
There is one major argument in favour of guava's choice: They 'contract break' is an implicit break - one would expect that .contains(null) works, and always returns false, but it's not explicitly stated in the javadoc that one must do this. Contrast to e.g. IdentityHashMap, which uses identity equivalence (a==b) and not value equality (a.equals(b)) in its .containsKey etc implementations, which explicitly goes against the javadoc contract as stated in the j.u.Map interface. IHM has an excellent reason for it, and highlights the discrepancy, plus explains the reason, in the javadoc. Guava isn't nearly as clear about their bizarre null behaviour, but, here's a crucial thing about null in java:
Its meaning is nebulous. Sometimes it means 'empty', which is bad design: You should never write if (x == null || x.isEmpty()) - that implies some API is badly coded. If null is semantically equivalent to some value (such as "" or List.of()), then you should just return "" or List.of(), and not null. However, in such a design, list.contains(null) == false) would make sense.
But sometimes null means not found, irrelevant, not applicable, or unknown (for example, if map.get(k) returns null, that's what it means: Not found. Not 'I found an empty value for you'). This matches with what NULL means in e.g. SQL. In all those cases, .contains(null) should be returning neither true nor false. If I hand you a bag of marbles and ask you if there is a marble in there that is grue, and you have no idea what grue means, you shouldn't answer either yes or no to my query: Either answer is a meaningless guess. You should tell me that the question cannot be answered. Which is best represented in java by throwing, which is precisely what guava does. This also matches with what NULL does in SQL. In SQL, v IN (x) returns one of 3 values, not 2 values: It can resolve to true, false, or null. v IN (NULL) would resolve to NULL and not false. It is answering a question that can't be answered with the NULL value, which is to be read as: Don't know.
In other words, guava made a call on what null implies which evidently does not match with your definitions, as you expect .contains(null) to return false. I think your viewpoint is more idiomatic, but the point is, guava's viewpoint is different but also consistent, and the javadoc merely insinuates, but does not explicitly demand, that .contains(null) returns false.
That's not useful whatsoever in fixing your code, but hopefully it gives you a mental model, and answers your question of "why does it work like this?".
I was updating the dependencies to Spring 5 in my project and was bombarded with compilation errors where the method definition of findOne() has been replaced by findById() which now returns an Optional (correct me if I am wrong).
While refactoring, I came across multiple approaches that I can choose to adopt, and I would therefore like some input on which one is to be preferred.
1st approach:
ExpectedPackage ep = expectedPackageRepository.findById(1).orElse(null);
if(ep != null){
ep.setDateModified(new Date());
expectedPackageRepository.saveAndFlush(ep);
}
2nd approach:
Optional<ExpectedPackage> ep = expectedPackageRepository.findById(1);
if(ep.isPresent()){
ep.get().setDateModified(new Date());
expectedPackageRepository.saveAndFlush(ep.get());
}
Or is there a third and better approach that I have missed? I went through several questions and a couple of articles, but I did not find a clear answer.
You can also do:
expectedPackageRepository.findById(1).ifPresent(
ep -> {
ep.setDateModified(new Date());
expectedPackageRepository.saveAndFlush(ep);
}
);
Ideally, you would also extract the part between brackets ({}) to a separate method. Then, you could write like this:
expectedPackageRepository.findById(1).ifPresent(this::doSomethingWithEp);
Where:
void doSomethingWithEp(ExpectedPackage ep) {
ep.setDateModified(new Date());
expectedPackageRepository.saveAndFlush(ep);
}
You can read the documentation of ifPresent here: https://docs.oracle.com/javase/8/docs/api/java/util/Optional.html#ifPresent-java.util.function.Consumer-
As it states, it will perform the specified action if the value is present and do nothing otherwise.
Yes, there are other approaches.
If you absolutely expect there always to be a value, then use Optional::orElseThrow to throw an Exception if a null appears.
If you expect a null to possibly arrive, and have an alternative instance available as a fall-back option, use Optional::orElse.
If the fall-back instance is not on hand, but you have a function to call to provide a fall-back instance, use Optional::orElseGet.
If you don’t care about receiving a null, and want to do nothing when a null arrives, use Optional::ifPresent. Pass the block of code to be run if a value arrives.
If you only care if a value arrives that meets some requirement, use Optional::filter. Pass a Predicate defining your requirement. For example, we care only if an Optional< String > contains text and that text has the word purple in it: myOptional.filter( s -> s.contains( "purple" ) ).ifPresent( this::print ) ;. If null received, our desired operation (a call to print in this example) never happens. If a value was received but failed to meet our predicate, our desired operation never happens.
Doing if( myOptional.isPresent() ) { SomeClass x = myOptional.get() ; … } is valid, and safe. But this is not the original intent of Optional as it is basically the same as doing an old-fashioned null-check if ( null == x ) { … }. The other methods on Optional provide a more clear and elegant way to express your intentions towards a possible null arriving.
The other answer is basically some refactoring of your second approach, which has nothing wrong per-se, it's just a matter of style. Of course chaining and extraction to a separate method will make this a lot more readable and clear, no doubt (+1 from me), especially since the correct usage of ifPresent.
I'd just add here that get, well, was seen as somehow a design error ( or may be a bad method name, probably that came from guava mindset ). Using get even if it documented to throw an Exception when that value is missing is somehow weird ( if you think getters here, you would not expect a getter to throw an Exception). And you would not expect that get needs to be called after isPresent, at least not on the very first interactions with Optional. Thus get was proposed to be deprecated ( and hopefully removed ), thus java-10 adds a better addition orElseThrow() - this makes sense right after you read it, cause the throwing part is in the name of the method, so no surprises.
Also, someone should tell you about that usage of new Date() that when used with Optional from java-8 just looks weird, there are much better time/date related classes already.
I am also not very sure why you are updating a modified date manually, when there are spring annotations for that like PreUpdate/PrePersist.
you can also do:
Optional<ExpectedPackage> updatedPackage = expectedPackageRepository.findById(1).map(ep -> {
ep.setDateModified(new Date());
return expectedPackageRepository.saveAndFlush(ep);
});
Is this a valid (intended) usage of Optional type in Java 8?
String process(String s) {
return Optional.ofNullable(s).orElseGet(this::getDefault);
}
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:
link to video
link to slides
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.
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.
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.
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);
}
So I have a constructor with 5 different variables, where three of which might be null. It accepts user input and the user does not have to enter anything for three of the five attributes.
Therefore, if the user did not enter anything, the object is created using null for all missing values.
obj = new Object(String, null, null, null, String);
Now I am wondering what would be best practice to cope with this.
I can think of three different scenarios:
Deal with it only in the class using the constructor, i.e. always query whether the value is null (e.g. if(getSomeAttribute == null) { //do something }
Deal with it within the object class, i.e. always return some default value for each missing attribute instead of null
Deal with it within the object lcass, i.e. have helper-methods like isAttributeSet(), returning a boolean value indicating whether the attributes are set, that is: not null.
Although I have problems with the last two, as I think I might run into problems with default values, as sometimes it might hard to know if it is a default value; if I'd always check I could just as well check for null instead of inserting a default value first;
Same with the last one, if I have to check the helper-method, I could just as well check for null directly.
What my problem is with this situation, is that sometimes I might not be the one using the getter and setter methods; how should the one using it know there might be null attributes and which that are.
I know, I should document that within my object class, but still I am wondering if there is a "best practice" way to cope with this.
I believe it should be unusual to always check the documentary (or if there is none, the whole class) for something as simple as this.
Maybe I should not even start with null values within my constructor in the first place? But I think I would run into the same kinds of problems, anyway, so that would not really solve my problem
Read Bloch, Effective Java, 2nd ed. Item 2: "Consider a builder when faced with many constructor parameters."
Excellent advice in an excellent book.
Using a builder pattern would help with the constructor, however the main problem is to do with the design of the class - you want to be sure when you call a get/set method that it isn't one of the null/optional members.
You could use polymorphism to have two objects each with an interface that only exposes the getters and setters supported by the concrete implementation. This makes it impossible to call the wrong getters/setters and it is obvious what the intention is.