I want to know how generics work in this kind of situation and why
Set<? extends Foo<?>> set3 = set1;
is allowed but Set<Foo<?>> set2 = set1;
is not?
import java.util.HashSet;
import java.util.Set;
public class TestGenerics {
public static <T> void test() {
Set<T> set1 = new HashSet<>();
Set<?> set2 = set1; // OK
}
public static <T> void test2() {
Set<Foo<T>> set1 = new HashSet<>();
Set<Foo<?>> set2 = set1; // COMPILATION ERROR
Set<? extends Foo<?>> set3 = set1; // OK
}
}
class Foo<T> {}
extends Number> represents a list of Number or its sub-types such as Integer and Double. Lower Bounded Wildcards: List<? super Integer> represents a list of Integer or its super-types Number and Object.
super is a lower bound, and extends is an upper bound.
super T denotes an unknown type that is a supertype of T (or T itself; remember that the supertype relation is reflexive). It is the dual of the bounded wildcards we've been using, where we use ? extends T to denote an unknown type that is a subtype of T .
Simply said, this is because Set<? extends Foo<?>>
is covariant (with the extends
keyword). Covariant types are read-only and the compiler will refuse any write action, like Set.add(..)
.
Set<Foo<?>>
is not covariant. It does not block write or read actions.
This...
Set<Foo<String>> set1 = new HashSet<>();
Set<Foo<?>> set2 = set1; // KO by compiler
... is illegal because otherwise I could for example put a Foo<Integer>
into set1
via set2
.
set2.add(new Foo<Integer>()); // Whoopsie
But...
Set<Foo<String>> set1 = new HashSet<>();
Set<? extends Foo<?>> set3 = set1; // OK
... is covariant (extends
keyword), so it is legal. For example, the compiler will refuse a write operation like set3.add(new Foo<Integer>())
, but accept a read operation like set3.iterator()
.
Iterator<Foo<String>> fooIterator = set3.iterator(); // OK
set3.add(new Foo<String>()); // KO by compiler
See these posts for a better explanation:
Perhaps the issue becomes clearer if you leave the generic parameter of Foo out of the equation.
Consider
final Set<Foo> set1 = new HashSet<>();
Set<Object> set2 = set1;
This makes the compile error more obvious. If this was valid, it would be possible to insert an object into set2, thus into set1 violating the type constraint.
Set<? extends Foo> set3 = set1;
This is perfectly valid because set1 would also accept types derived from Foo.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With