I have a class with strict, simple generic type:
public class GenericTools<T> {
private final Supplier<T> supplier;
private final Consumer<T> consumer;
public GenericTools(Supplier<T> supplier, Consumer<T> consumer) {
this.supplier = supplier;
this.consumer = consumer;
}
public Supplier<T> getSupplier() {
return supplier;
}
public Consumer<T> getConsumer() {
return consumer;
}
}
What is the exact reason for the fact that "capture of ?" cannot be used here and file does not compile?
GenericTools<?> tools = new GenericTools<>(Math::random, System.out::println);
tools.getConsumer().accept(tools.getSupplier().get());
Error:(27, 59) java: incompatible types: java.lang.Object cannot be converted to capture#1 of ?
With explicit <Double>
it compiles with no problems:
GenericTools<Double> tools = new GenericTools<>(Math::random, System.out::println);
tools.getConsumer().accept(tools.getSupplier().get());
I have used Java 1.8 to compile.
Please note that this is completely not a duplicate of Java generics “capture of ?”, where poster have no idea what we need to pass as "?"-typed argument when code requires it. In my case I am quite aware of capture mechanism, but stil type looking like supposed to work cannot be used. Most importantly, here I am asking about the exact reason (specification reference or something), not about what I should pass.
It's because the type
?
is invariant, andObject
is not a subtype of all types within the bounds of?
.I believe the Java 8 type inference is capable of inferring that
T
isDouble
on the RHS, but since you explicitly assign to aGenericTools<?>
on the LHS, the capture is of an unbounded type variable, which unifies with the unbounded variableT
which also has no bounds.Without any bounds, the
T
in the signatures ofSupplier::get
andConsumer::accept
are not guaranteed to be the same type -- remember, the type variable is invariant, as no co- or contra-variant bound is expressed. The erasure of theT
on theSupplier
side is justObject
, and the compiler cannot insert a runtime check that the runtime type is actually?
(because?
is not reifiable!). Therefore: the typeObject
cannot be implicitly converted to?
and compilation fails.