wildcard generics in HashSet constructor
The java HashSet
implementation has a constructor:
public HashSet(Collection<? extends E> c) {
map = new HashMap<E,Object>(Math.max((int) (c.size()/.75f) + 1, 16));
addAll(c);
}
Why it is Collection<? extends E> c
? This 开发者_JAVA百科isn't enough: Collection<E> c
?
The concept here is called variance (covariance, contravariance).
Let's say you have the following two classes:
class A {}
class B extends A {}
In this case, you can say that an instance of B
is an instance of A
. In other words, the following code is perfectly valid:
A instance = new B();
Now, generic classes in Java are, by default, invariant. That means that a List<B>
is not a List<A>
. In other words, the following code will not compile:
List<A> as = new ArrayList<B>(); // error - Type mismatch!
However, if you have an instance of B, sure you can add it to a list of A (because B extends A):
List<A> as = new ArrayList<A>();
as.add(new B());
Now, let's say you have a method that deals with lists of A by consuming its instances:
void printAs(List<A> as) { ... }
It would be tempting to make the following call:
List<B> bs = new ArrayList<B>();
printAs(bs); // error!
However, it won't compile! If you want to make such a call work, you have to make sure that the argument, List<B>
, is a subtype of the type expected by the method. This is done by using covariance:
void printAs2(List<? extends A> as) { ... }
List<B> bs = new ArrayList<B>();
printAs2(bs);
Now, this method takes an instance of List<? extends A>
, and it is true that List<B> extends List<? extends A>
, because B extends A
. This is the concept of covariance.
After this introduction, we can go back to the constructor of HashSet you mention:
public HashSet(Collection<? extends E> c) { ... }
What this means is that the following code will work:
HashSet<B> bs = new HashSet<B>();
HashSet<A> as = new HashSet<A>(bs);
It works because HashSet<B> is a HashSet<? extends A>
.
If the constructor were declared as HashSet(Collection<E> c)
, then the second line on the wouldn't compile, because, even if HashSet<E> extends Collection<E>
, it is not true that HashSet<B> extends HashSet<A>
(invariace).
This is because when HashMap can contain and object that inherits from E, so you want to be able to pass a collection of objects of any type that inherits E, not just E.
If it were Collection, then you wouldn't be able to pass an ArrayList<F>
, where F extends E, for example.
精彩评论