Consider this small test class:
import java.util.List;
public abstract class Test {
// CAN modify this constructor interface
public <T extends Runnable & Comparable<T>> Test(List<T> l) {
setList((List<Runnable>)l); // <-- (a) warning
setList(l); // <-- (b) error
}
// CANNOT modify this interface
public abstract void setList(List<Runnable> l);
}
This succinctly represents my problem, in that I want to use generic methods which take objects of type T (which are both Runnable and Comparable<T>) like Test‘s constructor, but I am constrained to using other methods with interfaces like that of setList to take collections of T.
(a) Why does the compiler warn about an unchecked conversion to List<Runnable> when l is an instance of List<T> where T extends, and has type erasure, of Runnable (as per JLS SE7 §4.6)?
(b) The compiler raises the following error:
error: method setList in class Test cannot be applied to given types;
setList(l); // <-- compiler error
^
required: List<Runnable>
found: List<T> reason: actual argument List<T> cannot be converted to
List<Runnable> by method invocation conversion
where T is a type-variable:
T extends Runnable,Comparable<T> declared in constructor <T>Test(List<T>)
My understanding here is that method invocation conversion couldn’t convert T to Runnable because perhaps this is considered a narrowing operation, but this is counter-intuitive, as I expected it could as T intersects both Runnable and Comparable<T>.
Must I resort to unchecked conversions (a) of instances of T to List<Runnable> in this case?
EDIT
The answer turns out to be yes, as Bhesh has pointed out below, generic types are invariant in Java. If I could use the following, error (b) would not occur:
public abstract void setList(List<? extends Runnable> l);
For more information, see this excellent tutorial and this section on capture conversion in the JLS SE7 §5.1.10.
a)
Tis-aRunnablebutList<T>is-not-aList<Runnable>. Generic types are invariant in Java.b) Reason is the same as the above. Method parameters are also invariant in Java. (Note: The method arguments are covariant.)
The cast in
setList((List<Runnable>)l);should be safe, because we know thatTis always going to be something that implementsRunnable. This is the type of situation for which the annotation@SuppressWarnings("unchecked")exists.Still a problem could be (depending upon your situation) that you are invoking an overridable method from a constructor.