2011-07-01 2 views

ответ

5

Концепция здесь называется variance (ковариация, контравариантность).

Допустим, у вас есть следующие два класса:

class A {} 
class B extends A {} 

В этом случае, вы можете сказать, что экземпляр B является экземпляром A. Другими словами, справедлив следующий код:

A instance = new B(); 

Теперь общие классы в Java по умолчанию являются инвариантными. Это означает, что List<B> не является List<A>. Другими словами, следующий код не будет компилироваться:

List<A> as = new ArrayList<B>(); // error - Type mismatch! 

Однако, если у вас есть экземпляр B, что вы можете добавить его в список А (потому что B распространяется A):

List<A> as = new ArrayList<A>(); 
as.add(new B()); 

Теперь, скажем, у вас есть метод, который имеет дело со списками А, потребляя его экземпляры:

void printAs(List<A> as) { ... } 

было бы заманчиво сделать следующий вызов:

List<B> bs = new ArrayList<B>(); 
printAs(bs); // error! 

Однако он не скомпилируется! Если вы хотите сделать такую ​​работу вызова, вы должны убедиться, что аргумент List<B> является подтипом типа, ожидаемого этим методом. Это делается с помощью ковариации:

void printAs2(List<? extends A> as) { ... } 
List<B> bs = new ArrayList<B>(); 
printAs2(bs); 

Теперь этот метод принимает экземпляр List<? extends A>, и это правда, что List<B> extends List<? extends A>, потому что B extends A. Это концепция ковариации.


После этого введения, мы можем вернуться к конструктору HashSet вы упоминаете:

public HashSet(Collection<? extends E> c) { ... } 

Что это означает, что следующий код будет работать:

HashSet<B> bs = new HashSet<B>(); 
HashSet<A> as = new HashSet<A>(bs); 

Он работает т.к. HashSet<B> is a HashSet<? extends A>.

Если конструктор был объявлен как HashSet(Collection<E> c), то вторая линия на не обобщать, потому что, даже если HashSet<E> extends Collection<E>, это не правда, что HashSet<B> extends HashSet<A> (invariace).

2

Это происходит потому, что когда HashMap может содержать и объект, который наследует от Е, так что вы хотите, чтобы иметь возможность передать коллекцию объектов любого типа, который наследуется E, а не только Е.

Если бы это было Collection, то вы не сможете передать ArrayList<F>, где F расширяет E, например.

Смежные вопросы