class User implements UserDetails
Why doesn't it work?
public Optional<UserDetails> getUserDetails(String username) {
Optional<User> userOptional = userService.findByUsername(username);
return userOptional;
}
.. but it works.
public Optional<UserDetails> getUserDetails(String username) {
Optional<User> userOptional = userService.findByUsername(username);
return userOptional.map(user -> user);
}
This also doesn't work.
public Optional<UserDetails> getUserDetails(String username) {
Optional<User> userOptional = userService.findByUsername(username);
return userOptional.stream().map(user -> user).findFirst();
}
The error occurs because Optional<User> cannot be converted to Optional<UserDetails>
CodePudding user response:
The second code works because Optional.map is declared like this:
public <U> Optional<U> map(Function<? super T,? extends U> mapper)
It is a generic method declared to return any Optional<U>. In your code, the generic type parameter U is inferred from the return type of the getUserDetails method to be UserDetails. If you specify the type parameter explicitly, it becomes:
return userOptional.<UserDetails>map(user -> user);
Substituting User for T and UserDetails for U, we can see that map takes a parameter of type Function<? super User,? extends UserDetails>, i.e. a function that takes a User and returns a UserDetails. user -> user, the identity function, is such a function, because a User is always a UserDetails. Therefore the code compiles.
The first code does not compile because you cannot convert from Optional<User> to Optional<UserDetails>.
Note that there is no contravariance, or covariance, for that matter, involved in the second code. It compiles simply because map returns Optional<U>, where U is an inferred type parameter.
To be able to convert from Optional<User> to Optional<UserDetails>, you need covariance. Java only has use-site variance, so you can only achieve covariance by adding ? extends to Optional<UserDetails>.
public Optional<? extends UserDetails> getUserDetails(String username) {
Optional<User> userOptional = userService.findByUsername(username);
return userOptional;
}
Similarly, for Stream.map, it is also a generic method.
<R> Stream<R> map(Function<? super T,? extends R> mapper)
However, in your third code, since the last call in return statement's expression is not map, the return type of the getUserDetails method is not used to infer R. R is simply inferred to be User, and findFirst returns Optional<User>.
