findAny orElse after filter
Solution 1
As Tagir explained, using orElse(expression)
always causes the evaluation of expression
before invoking the method orElse
and you have to use orElseGet(() -> expression)
instead to defer the evaluation of the expression.
However, this is an unnecessary use of the Stream
API. If you want to evaluate a single item, you don’t need to create a single-element stream just to call findAny
afterwards. You can create an Optional
in the first place:
return Optional.of(getObjectAttributeValue(product, matchCriteria.getFieldName()))
.map(o -> isIngredientRestricted(matchCriteria, (List<String>)o))
.filter(b -> b)
.orElseGet(() -> isCommercialHierarchyInfoRestricted(product, matchCriteria));
However, even that is an unnecessary complication compared to the equivalent ordinary Java language construct:
return isIngredientRestricted(matchCriteria,
(List<String>)getObjectAttributeValue(product, matchCriteria.getFieldName()))
|| isCommercialHierarchyInfoRestricted(product, matchCriteria);
This does exactly the same without the need for additional APIs nor lambda expressions. The ||
operator also guarantees that the second expression won’t get evaluated, if the first one evaluates to true
.
Solution 2
You actually need to use orElseGet
:
.findAny().orElseGet(() -> isCommercialHierarchyInfoRestricted(product, matchCriteria));
In Java method argument is always evaluated prior to method call even if it's unnecessary inside the method, so you cannot avoid evaluating the orElse
argument. That's why orElseGet
exists: its argument is the function and the function can be not executed at all when it's not necessary.
Comments
-
paul almost 2 years
I'm using Stream filter
findAny.orElse
, but it's not working as I expect, so I presume I'm not understanding how really works. Here my codereturn Stream.of(getObjectAttributeValue(product, matchCriteria.getFieldName())) .map(o -> isIngredientRestricted(matchCriteria, (List<String>) o)) .filter(Boolean::valueOf) .findAny().orElse(isCommercialHierarchyInfoRestricted(product, matchCriteria));
Basically what I was expecting was that if the first map emit a Boolean false, then it will be filter so then the findAny would not find any optional, so the orElse would be invoked. But even having a true in the filter the isCommercialHierarchyInfoRestricted is invoked.
Any idea what I'm doing wrong?