Both <?>
and <E>
are unknown types; but all List<?>
s have elements of some type, so you are able to call a method whose parameter is List<E>
, because there is some unknown type which matches it.
Note that you can't do this:
public static void swap(Object object, List<?> list) {
swapHelper(object, list); // Compiler error: object not in bounds of list.
}
private static <E> void swapHelper(E object, List<E> list) {
swap(object, list); // OK.
}
because you now don't know if Object
is within the bounds of List<?>
.
Nor can you add new non-null values to the list:
private static <E> void swapHelper(List<E> list) {
list.add(new E()); // Can't create an instance of type variable.
swap(list);
}
but you can add values which you take from the list, since they are known to be within the bounds of the list:
private static <E> void swapHelper(List<E> list) {
list.add(list.get(0));
swap(list);
}
so it is type safe to do invoke the original swapHelper
from swap
, since you can't cause something which is not castable to E
(whatever that type is) to the list.
Similarly with tW
and tE
:
- If you assign
tW = tE
, you can't add anything to tW
other than null
, so you can't put tE
in a state where it contains anything other than instances of E
.
- If you were allowed to assign
tE = tW
, you could add non-null instances of E
to tE
. This might mean that you could add an instance of the wrong class to tW
, potentially leading to runtime type errors. As such, this assignment is forbidden.
Remember that assigning a list doesn't cause the list to be copied: if you assign tE = tW
, then tE == tW
, so all changes applied to tE
are also visible via tW
, since they are same instance.