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 isn’t enough: Collection<E> c?
The concept here is called variance (covariance, contravariance).
Let’s say you have the following two classes:
In this case, you can say that an instance of
Bis an instance ofA. In other words, the following code is perfectly valid:Now, generic classes in Java are, by default, invariant. That means that a
List<B>is not aList<A>. In other words, the following code will not compile:However, if you have an instance of B, sure you can add it to a list of A (because B extends A):
Now, let’s say you have a method that deals with lists of A by consuming its instances:
It would be tempting to make the following call:
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:Now, this method takes an instance of
List<? extends A>, and it is true thatList<B> extends List<? extends A>, becauseB extends A. This is the concept of covariance.After this introduction, we can go back to the constructor of HashSet you mention:
What this means is that the following code will work:
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 ifHashSet<E> extends Collection<E>, it is not true thatHashSet<B> extends HashSet<A>(invariace).