How can I implement a method that accepts a consumer <Optional <T>> that is contravariant in T?
In the following example, I can pass Consumer<Optional<Integer> to foo , but not Consumer<Optional<Number>> . On the other hand, I can pass any type to foo2 , but then I cannot name the accept method of the consumer from the body of the method. Is there a way to change the foo method so that it works? My initial intuition was to try void foo(Consumer<Result<? super T>> c) , but this does not seem to mean what I would suggest.
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 not special in terms of type systems: we know that Optional only has a provider method ( Optional.get() ) and that it does not have consumer methods (for example Optional.set(T) ); but the compiler does not.
So, the compiler will not allow you to pass an Optional<Integer> where Optional<Number> is required: this prevents you from ever calling this mythical set method if you passed in Integer instead.
The only way is to change Optional<T> to Optional<S> , where S is a supertype of T You can do this via:
- Casting, which, as you know, is safe due to the invariability of
Optionaland its lack of consumer methods; but you get an unverified warning (which is really great for suppression due toOptionalproperties). - Creating a new
Optionalcorrect type may be cleaner, but it has an overhead of execution time to create a new instance.
To write such a thing in a method, you need to write it as a static method (perhaps in the test class, but it can be somewhere else); A system such as Java is not expressive enough to write the required restrictions on the signature of the instance method:
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 is called like this (using the values โโof cri , crn and T from the question code):
foo3(cri, t); // compiles foo3(crn, t); // compiles