Compilation fails when using a lambda for Supplier

2019-02-19 18:42发布

问题:

Why does this not compile? :

import java.util.Optional;

public class Demo {

    Optional<? extends SomeValue> getOption() {
        return Optional.empty();
    }

    void exposure() {
        SomeValue someValue = getOption().orElseGet(() -> new SomeValue());
    }
}

class SomeValue {}

It works without problem when getOption() would return a Optional<SomeValue>.

回答1:

This is the result of a rather restricted method signature:

public T orElseGet(Supplier<? extends T> other) {

In your case, T is ? extends SomeValue which is an unknown type being assignable to SomeValue (but may be a subclass of it). The signature of orElseGet implies that your supplier is allowed to return a subtype of T, having the overall result type of T but it does not allow to do a widening to a common base type of T and the type your supplier returns (If your supplier returns SomeValue and T is ? extends SomeValue, the common base type would be SomeValue).

You can fix this by inserting an operation which allows widening of the type:

SomeValue someValue = getOption()
  .map(Function.<SomeValue>identity()).orElseGet(() -> new SomeValue());

The identity function does not change the value, but the mapping operation allows to pass in a function consuming a broader type, i.e. the mapping function may declare to consume SomeValue when the actual input type is ? extends SomeValue and the return type is SomeValue.


But generally, to avoid such problems, methods should not have wildcards in their return types, so change

Optional<? extends SomeValue> getOption()

to

Optional<SomeValue> getOption()

See http://docs.oracle.com/javase/tutorial/java/generics/wildcardGuidelines.html

Using a wildcard as a return type should be avoided because it forces programmers using the code to deal with wildcards.



回答2:

Optional<T>.orElseGet is specified to accept a Supplier<? extends T>. But the meaning of Optional<? extends SomeValue> is that it could be an Optional<MySubClassOfSomeValue>, in which case a Supplier<SomeValue> would not be a Supplier<? extends MySubClassOfSomeValue>.

orElseGet must return a subtype of the element type of the Optional, and ? extends SomeValue may be something other than SomeValue.