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>
.