Java 8's orElse not working as expected - java

Consider the following method which returns a field if it exists or recursively calls itself until the field is found:
private Field getField(Class<?> clazz, String p) {
Optional<Field> field = Arrays.stream(clazz.getDeclaredFields())
.filter(f -> p.equals(f.getName()))
.findFirst();
return field.isPresent() ? field.get() : getField(clazz.getSuperclass(), p);
}
While this works, I thought I could shorten it to:
private Field getField(Class<?> clazz, String p) {
return Arrays.stream(clazz.getDeclaredFields())
.filter(f -> p.equals(f.getName()))
.findFirst()
.orElse(getField(clazz.getSuperclass(), p));
}
But the strange thing is that the .orElse part seems to always be called.
What am I missing here?

The arguments for a method are always evaluated before the method is called. You want orElseGet which takes a Supplier that will only be invoked if the Optional is not present:
private Field getField(Class<?> clazz, String p) {
return Arrays.stream(clazz.getDeclaredFields())
.filter(f -> p.equals(f.getName()))
.findFirst()
.orElseGet(() -> getField(clazz.getSuperclass(), p));
}

Related

JAVA8 Optional and Lambdas

Suppose I have this class model hierarchy:
public class A {
private Integer id;
private List<B> b;
}
And:
public class B {
private Integer id;
private List<C> c;
}
And finally:
public class C {
private Integer id;
}
And a simple Service:
#Service
public class doSome {
public void test() {
Optional<A> a = Optional.of(a) // Suppose a is an instance with full hierarchy contains values
/** *1 **/ // what I want to do
}
}
Now what I want to do at the *1 position is to use lambda to extract the Optional value (if exixsts) and map the subrelation to obtain all id of the C class. I have tried something like this:
public void test() {
Optional<A> a = Optional.of(a);
List<Integer> temp = a.get().getB()
.stream()
.map(x -> x.getC())
.flatMap(List::stream)
.map(y -> y.getId())
.collect(Collectors.toList()); // works
}
Now I would like to put inside my lambda the a.get().getB(), I have tried several ways but with no luck.
Anyway I don't understand why I can't use two consecutive map like
.map(x -> x.getC())
.flatMap(List::stream)
.map(y -> y.getId())
without using flatMap(List::stream) in the middle... the map doesn't return a new Stream of Type R (class C in this case)? Why I have to Stream it again? where am I wrong?
----------------------- UPDATE ------------------
This is just an example, It's pretty clear that the Optional here is useless but in real case could comes by a findById() JPA Query.
Holger for this reasons I would put all inside a part of code, doing something like:
public <T> T findSome(Integer id) {
Optional<T> opt = repository.findById(id);
return opt.map(opt -> opt).orElse(null);
}
I have read here some solution like follows:
Optional.ofNullable(MyObject.getPeople())
.map(people -> people
.stream()
.filter(person -> person.getName().equals("test1"))
.findFirst()
.map(person -> person.getId()))
.orElse(null);
And I would like to adapt at my case but without no luck.
As of java-9 and newer, you can call Optional#stream:
List<Integer> temp = a.map(A::getB)
.stream()
.flatMap(Collection::stream)
.map(B::getC)
.flatMap(Collection::stream)
.map(C::getId)
.collect(Collectors.toList());
If you are stuck with java-8, you need to map to Stream (or return the empty one) and continue chaining:
List<Integer> temp = a.map(A::getB)
.map(Collection::stream)
.orElse(Stream.empty())
.map(B::getC)
.flatMap(Collection::stream)
.map(C::getId)
.collect(Collectors.toList());
Note: Optional<A> a = Optional.of(a) is not valid as a is already defined.

Defaulting Optional orElse with Optional.empty in Java 8

Java 8 here. I need to search two lists of POJOs for a string and want to use the Stream/Optional APIs correctly.
If the name appears in the first list ("lunches") then I want to return an optional containing it. Else, if the name appears in the second list ("dinners") then I want to return an optional containing it. Otherwise I want to return Optional.empty() if the name doesn't existing in either list. My best attempt thus far:
public class Restaurant {
private String id;
private String name;
private List<Food> lunches;
private List<Food> dinners;
public Optional<Food> findFoodByName(String name) {
return Optional.of(lunches.stream()
.filter(food -> food.getName()
.equalsIgnoreCase(name))
.findFirst())
.orElse(dinners.stream()
.filter(food -> food.getName()
.equalsIgnoreCase(name))
.findFirst());
// .orElse(null); TODO: how to return empty optional if neither in 'lunches' nor 'dinners'?
}
}
Can anyone help me cross the finish line here?
Combine both the list using Stream.of and check for element or return Optional.empty()
Stream.of(lunches, dinners)
.flatMap(List::stream)
.filter(s -> s.getName()
.equalsIgnoreCase(name))
.findFirst();
As per the suggestion from #Holger you can also use Stream.concat to concat two streams and then check for element
Stream.concat(lunches.stream(), dinners.stream())
.filter(s -> s.getName()
.equalsIgnoreCase(name))
.findFirst();
You can do like this too:
Optional<Food> firstTry = lunches.stream()
.filter(f -> f.getName().equalsIgnoreCase(name))
.findFirst();
return firstTry.map(Optional::of)
.orElse(dinners.stream()
.filter(f -> f.getName().equalsIgnoreCase(name)).findFirst());
Or in Java9
firstTry.or(() -> dinners.stream().filter(s -> s.equalsIgnoreCase(name)).findFirst());
As #Slaw commented correctly use of orElseGet() avoid eagerly computing.
Optional<Food> firstTry = lunches.stream().filter(...)...findFirst();
Supplier<Optional<Food>> secondTry = () -> dinners.stream()...findFirst();
and at the end
return firstTry.map(Optional::of).orElseGet(secondTry);

Find element in ArrayList by conditions and return him or null by lambda

I have collection of custom objects, and I need in my function find the correct one by id property, and return him. If element with that id won't found function return null. Can you help me fix my code? Here is it:
public MyObj find(long id) {
return myList.stream()
.filter(obj -> obj.getId() == id)
.map(obj -> {
return obj;
})
.findFirst()
.orElse(null);
}
I had error rendundant map call, and part of code with map function is gray. What is wrong with this? Thanks
There's no reason for including the map call, since it's not changing anything (it accepts a MyObj instance and returns the same instance).
public MyObj find(long id) {
return myList.stream()
.filter(obj -> obj.getId() == id)
.findFirst()
.orElse(null);
}

Filter only parameters that are present in the request

There are lots of articles that are about Java 8 lambda operations however I couldn't find what I need until now. I tried to convert them to my approach unfortunately I couldn't succeed
Imagine that you have request that comes in POJO such as ;
public class DummyRequest {
private String name;
private String surname;
private String country;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSurname() {
return surname;
}
public void setSurname(String surname) {
this.surname = surname;
}
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country= country;
}
}
During REST/SOAP request surname variable will be assigned as null.
List<Person> persons = Arrays.asList(
new Person("maria", "gambert", "italy"),
new Person("jack", "johson", "usa"),
new Person("johnson", "jack", "usa"),
new Person("kate", "julia", "spain"),
new Person("jack","bob","uk");
DummyRequest dr = new DummyRequest();
dr.setName("jack");
dr.setCountry("usa");
If I knew in advance that the surname field would be null, I could manage to filter the list like this, ignoring that field:
List<Person> result4 = persons.stream().
filter(x -> dummyRequest.getName().equals(x.getName())).
filter( x-> dummyRequest.getCountry().equals(x.getCountry())).
collect(Collectors.toList());
However I don't know which fields will be null and which will not. How could I instead filter my data according to non-null request parameters only?
If you want to only apply the filter for which the dummyRequest contains a non-null value, you would have to dynamically build your stream for the most efficient solution.
This could easily be done by implementing a helper method that conditionally applies a filter:
public static <T, V> Stream<T> filterIfNotNull(Stream<T> stream, V filterValue, Function<T, V> property) {
if (filterValue == null) {
return stream;
}
return stream.filter(t -> filterValue.equals(property.apply(t)));
}
(in your example T would always be Person and V would always be String, but this generic version allows more reusability without additional complexity at the call site)
Then the stream/collect can be implemented like this:
Stream<Person> personStream = persons.stream();
personStream = filterIfNotNull(personStream, dummyRequest.getName(), Person::getName);
personStream = filterIfNotNull(personStream, dummyRequest.getSurname(), Person::getSurname);
personStream = filterIfNotNull(personStream, dummyRequest.getCountry(), Person::getCountry);
List<Person> result4 = personStream.collect(Collectors.toList());
This technique guarantees that the null-check on the request's properties is only applied once.
I would define a static method for that, since you are duplicating the code so many times:
private static boolean nullableOrEqual(String left, String right) {
return left == null || left.equals(right);
}
And then the usage would be:
List<Person> result = persons.stream()
.filter(x -> nullableOrEqual(dr.getSurname(), x.getSurname()))
.filter(x -> nullableOrEqual(dr.getCountry(), x.getCountry()))
.filter(x -> nullableOrEqual(dr.getName(), x.getName()))
.collect(Collectors.toList());
If you want to filter only by the non-null properties of dummyRequest, you can simply add a null check to each Predicate:
List<Person> result4 =
persons.stream()
.filter(x -> dummyRequest.getSurname() == null || dummyRequest.getSurname().equals(x.getSurname()))
.filter(x -> dummyRequest.getName() == null || dummyRequest.getName().equals(x.getName()))
.filter(x -> dummyRequest.getCountry() == null || dummyRequest.getCountry().equals(x.getCountry()))
.collect(Collectors.toList());
You could create a checkNonNullProperties helper method that returns a Predicate<Person> that only checks for equality of non-null properties of your DummyRequest instance. You could use it as follows:
Predicate<Person> condition = checkNonNullProperties(
Arrays.asList(
dr.getCountry(),
dr.getName(),
dr.getSurname()),
Arrays.asList(
Person::getCountry,
Person::getName,
Person::getSurname));
List<Person> result = people.stream()
.filter(condition)
.collect(Collectors.toList());
The helper method:
private static <T> Predicate<T> checkNonNullProperties(
List<?> values,
List<Function<T, ?>> extractors) {
return IntStream.range(0, values.size()).mapToObj(i ->
(Predicate<T>) t -> {
Object value = values.get(i);
Object property = extractors.get(i).apply(t);
return value == null || value.equals(property);
})
.reduce(t -> true, Predicate::and);
}
The checkNonNullProperties method receives a list of values to check for equality and a list of functions that will extract the properties from the argument of the returned predicate. The extracted properties will be checked for equality against their corresponding values only for those values that are non-null.
I'm using an IntStream to drive iteration over both lists. In the mapToObj method I'm mapping the stream's int value to a predicate that returns true when the provided value is null or when it's equal to the extracted property.
In the end, these predicates are reduced to a final predicate via the Predicate::and operator. In the reduce call, I'm providing the identity predicate for the AND operator, which is t -> true (always returns true).

method ref from lambda

In one of the method we used lambda expression (below) nesting streams.
return req.gUs().stream()
.map((**cfg) -> {
.filter(Optional::isPresent)
.map(Optional::get);
I want to move part of the code using some helper methods and method reference (below).
(cfg) -> {
return rsp.getUs().stream()
.filter(result.getId())
.map((result) -> gEvent(index, Builder, cfg));
Any suggestions are greatly appreciated.
You can make a method, that returns a Function:
return req.getUs().stream()
.map(myFunction(rsp, index, headerBuilder))
.flatMap(stream -> stream)
.filter(Optional::isPresent)
.map(Optional::get);
private Function<CFGType, GenerateReturnType> myFunction(RspType rsp, IndexType index, HeaderType header){
return (cfg) -> {
return rsp.getPerUs().stream()
.filter((result) -> cfg.getId() == result.getId())
.filter((result) -> result.getCode() == ResultCode.SUCCESS)
.map((result) -> generateEvent(index, headerBuilder, cfg));
}
}
Or you could use a method reference if the rsp, index and header are fields:
return req.getUs().stream()
.map(this::myFunction)
.flatMap(stream -> stream)
.filter(Optional::isPresent)
.map(Optional::get);
private GenerateType myFunction(CFGType cfg) {
return rsp.getUs().stream()
.filter((result) -> cfg.getUsChId() == result.getChId())
.filter((result) -> result.getResultCode() == ResultCode.SUCCESS)
.map((result) -> generateEvent(index, headerBuilder, cfg));
}

Categories

Resources