开发者

wildcard generics in HashSet constructor

开发者 https://www.devze.com 2023-03-16 12:47 出处:网络
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));

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.

0

精彩评论

暂无评论...
验证码 换一张
取 消