2014-03-23 4 views
1

Рассмотрим следующие классы:Использование подстановочных знаков на интерфейсах

interface Notifiable { 

} 

class NotifiableImpl1 implements Notifiable { 

} 

class NotifiableImpl2 implements Notifiable { 

} 

class NotifiableImpl3 implements Notifiable { 

} 

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

Set<Notifiable> set = new HashSet<>(); 
set.add(new NotifiableImpl1()); 
set.add(new NotifiableImpl2()); 
set.add(new NotifiableImpl3()); 

Однако, следующий код не работает:

Set<? extends Notifiable> set2 = new HashSet<>(); 
set2.add(new NotifiableImpl1()); 
set2.add(new NotifiableImpl2()); 
set2.add(new NotifiableImpl3()); 

Я понимаю, что это не сработает, потому что только можно добавить один конкретный подтип pe от Notifiable до set2, но как получается, что следующий код также не работает?

Set<? extends Notifiable> set2 = new HashSet<>(); 
set2.add(new NotifiableImpl1()); 

А может быть, более интересно, почему делает следующие работы?

Set<Notifiable> set = new HashSet<>(); 
set.add(new NotifiableImpl1()); 
set.add(new NotifiableImpl2()); 
set.add(new NotifiableImpl3()); 

Set<? extends Notifiable> set3 = set; 
+0

Я думаю, вы можете найти ответы [здесь] (http://stackoverflow.com/questions/2723397/java-generics-what-is-pecs). Мне нравятся эти утверждения: «1-е использование расширенного подстановочного знака, когда вы получаете только значения из структуры , используйте суперсимвол, когда вы ставите значения в структуру и не используете подстановочный знак, когда вы оба получаете и ставите.' – nachokk

+0

@ nachokk Этот вопрос не о супер vs extends. Хотя ответ может быть похож, это не значит, что вопрос схож. – skiwi

+0

Это правда, это не тот же вопрос, но то же объяснение, что и ответ. – nachokk

ответ

3

Первый код совершенно применим. Так как вы можете иметь ссылку супер типа держать объект подкласса, как в ниже задания:

Notifiable notifiable = new NotifiableImpl1(); 

Точно так же вы можете добавить NotifiableImpl объект в Set<Notifiable>. Этот набор может содержать объект любого подтипа.

Второй код недействителен согласно PECS rule. В основном Set<? extends Notifiable> является производителем Notifiable, а не потребителем. Вы не можете добавить к нему какой-либо объект подтипа. Этот набор может фактически содержать ссылку на HashSet<NotifiableImpl1>, и вы не можете добавить объект NotifiableImpl2 к такому набору. Это не сработает во время выполнения, если это разрешено компилятором. Фактически вы не можете добавить ничего, кроме null, к таким типам, потому что фактический тип, который будет содержать Set, до сих пор неизвестен компилятору.

Третий код также действителен. A Set<? extends Notifiable> означает Set неизвестного типа, который простирается от Notifiable. Таким образом, в следующем коде:

Set<Notifiable> set = new HashSet<>(); 
Set<? extends Notifiable> set3 = set; 

Вы просто присваиваете конкретную параметризированную ссылку типа для ограниченного типа подстановки. Поскольку Notifiable является допустимой заменой для ? extends Notifiable, это назначение имеет смысл.

Причина существования типов подстановочных знаков заключается в том, чтобы разрешить одну контрольную точку для разных видов объектов. Это общее полиморфное правило. Без подстановочных знаков для Set<Notifiable> недействительно указывать на HashSet<NotifiableImpl1>, просто потому, что общие типы являются инвариантными.

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

  • Нет связанных вопросов^_^