简体   繁体   中英

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 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:

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> List<? extends A> , and it is true that List<B> extends List<? extends A> 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> 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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM