In the following sample, I can pass a Consumer<Optional<Integer>
to foo
, but not a Consumer<Optional<Number>>
. On the other hand, I can pass either type to foo2
, but then I can't call the accept method of the consumer from the method body. Is there a way to change the foo
method so that this works? My initial intuition was to try void foo(Consumer<Result<? super T>> c)
but that apparently doesn't mean what I would assume.
import java.util.Optional;
import java.util.function.Consumer;
public class test<T> {
public void foo(Consumer<Optional<T>> c) {
Optional<T> t = null;
c.accept(t); // compiles
}
public void foo2(Consumer<? extends Optional<? super T>> c) {
Optional<T> t = null;
c.accept(t); // doesn't compile
}
public static void bar() {
test<Integer> t = null;
Consumer<Optional<Number>> crn = null;
Consumer<Optional<Integer>> cri = null;
t.foo(cri); // compiles
t.foo(crn); // doesn't compile
t.foo2(cri); // compiles
t.foo2(crn); // compiles
}
}
The reason for this is that Optional
isn't special from the point of view of the type system: we know that Optional
only has a provider method (Optional.get()
) and that it has no consumer methods (like Optional.set(T)
); but the compiler doesn't.
So, the compiler it won't let you pass an Optional<Integer>
where an Optional<Number>
is required: it is preventing you from ever calling that mythical set
method, in case you passed in a Double
instead of an Integer
.
The only way around this is to change the Optional<T>
into an Optional<S>
, where S
is a supertype of T
. You can do this by either:
Optional
and its lack of consumer methods; but you get an unchecked warning (which is actually fine to suppress, because of the properties of Optional
).Optional
of the right type - maybe more pure, but has the runtime overhead of creating the new instance.In order to write such a thing in a method, you would have to write it as a static method (probably in the test
class, but it could be elsewhere); Java's type system isn't expressive enough to be able to write the required constraints on an instance method's signature:
public static <T, S extends T> void foo3(Consumer<Optional<T>> c, test<S> test) {
Optional<S> s = null;
@SuppressWarnings("unchecked") // Safe because of properties of Optional.
Optional<T> t = (Optional<T>) (Optional<?>) s;
c.accept(t);
}
and invoke like this (using the values of cri
, crn
and t
from the question code):
foo3(cri, t); // compiles
foo3(crn, t); // compiles