2013-06-05 4 views
1

Согласно книге «Эффективная Java» Джошуа Блоха, существует правило о том, как использовать ограниченные подстановочные знаки в дженериках. Это правило - PECS (Producer-Extends, Comsumer-Super). Когда я изучаю следующий пример:Java Generics (ограниченные подстановочные знаки)

Stack<Number> numberStack = new Stack<Number>(); 
Iterable<Integer> integers = ... ; 
numberStack.pushAll(integers); 

Я понимаю, что это правило идеально подходит в этом примере. Я должен объявить метод pushAll, как в следующем примере:

// Wildcard type for parameter that serves as an E producer 
public void pushAll(Iterable<? extends E> src) { 
    for (E e : src) 
    { 
     push(e); 
    } 
} 

Но что произойдет, если у меня есть следующий пример?

Stack<Integer> integerStack = new Stack<Integer>(); 
Iterable<Number> numbers = ... ; 
integerStack.pushAll(numbers); 

Я должен объявить pushAll как это следующим образом:

public void pushAll(Iterable<? super E> src) { 
    for (E e : src) 
    { 
     push(e); 
    } 
} 

Согласно PECS править выше заявление является неправильным. Но я хочу иметь StackInteger s и пройти к этому Stack a Number. Почему бы не сделать это?
Почему я должен всегда использовать ключевое слово extends? Почему использование super неверно?
Конечно же то же, что и для comsumer. Почему потребитель всегда должен быть super?


PS: Чтобы быть более конкретным, вы можете найти этот вышеприведенный пример в секторе «Пункт 28» упомянутой книги.

+0

Когда вы нажимаете - вы выступаете в качестве консультанта, а не производителя, поэтому вам нужно использовать ключевое слово super. – Alexandr

+0

Согласно книге, я продюсер. Комментарий «Тип подстановочного знака для параметра, который служит производителем E», является автором. Писатель говорит, что я comsumer, когда я тяну за Stack! Первые два образца взяты из книги (copy-paste). Третий пример - мой. – LiTTle

ответ

3

Когда вы объявляете Stack<Foo>, вы имеете в виду стек фонов или подклассы Foo. Например, вы ожидаете, что сможете разместить String в Stack<Object>. Другой способ неверен, вы не сможете вставить другой объект в Stack<String>.

В вашем примере вы объявляете Stack<Integer>. Вы должны уметь помещать целые числа в этот стек, но не другие Numbers (например, Double), которые вы хотели бы, если бы вы объявили параметр <? super E>. Поэтому метод put должен иметь параметр типа <? extends E>.

0

В способе pushAll вы не проходите тип E, но любой тип, который распространяется на E. Итак, вместо передачи Iterable из Number s вы можете передать любой Iterable типа, который расширяет Number.

Оригинальный пример использует Number типа, потому что вы можете передать любой тип, который является подклассом Number, как Integer, BigDecimal и так далее.

В вашем примере вы делаете это наоборот. Вы используете Integer, чтобы объявить свой Stack. Поэтому pushAll сможет принимать только те классы, которые расширены на Integer. Вы не сможете использовать Numbers (или любой другой класс, потому что Integer - последний класс).

1

Попытка сохранить произвольные номера в стеке не может работать, поскольку число может быть чем-то другим, что целое.Таким образом, ваш пример не имеет большого смысла.

Вы бы использовали супер, когда объект попадает в качестве потребителя, то есть когда экземпляры общего типа объекта передаются в качестве аргументов методам объекта. Например:

Collections.sort(List<T>, Comparator<? super T>) 

В этом примере метод рода принимает T экземпляры из коллекции, и передает их в качестве аргумента в compare(T o1, T o2) компаратора.

Сравните это с вашим первым примером, где Iterable src является производителем. Метод pushAll() вызывает метод Iterable, который генерирует экземпляры T. В этом случае итерабель является производителем, поэтому использование ? extends T

0

Прежде всего следует отметить, что Integer расширяет число, поэтому вы не следует толкать объекты Number в стек целых чисел. Однако первый образец будет работать с целыми, поплавками, BigDecimal и всеми другими подклассами Number.

0

Ваш пример не имеет большого смысла. Конструкция, подобная <? extends Number>, означает, что число и каждый тип разрешены, что неточно. Таким образом, вы определяете верхнюю и нижнюю границы, от номера типа до самого определенного. Другой способ: <? super Number> означает, что Number и любой из его суперсов разрешены. Поскольку количество расширяет объект и реализует Serializable следующие три типа допускаются:

  1. java.lang.Number
  2. java.lang.Object
  3. java.io.Serializable

В вашем примере вы объявляете общий тип Stack<Integer>. Рассмотрим следующее.

  1. Ваш стек никогда не быть в состоянии держать предметы любого супер типа Целого
  2. Ваш стек никогда не быть в состоянии держать предметы любого подтипа Целого, так как Integer класса является окончательным и, таким образом, он не может быть подклассом.

Итак, если вы хотите, чтобы объявить общий тип Stack<Integer>, ваш итерацию имеет тип Iterable<Integer> и, таким образом, ваш стек может только хранения элементов типа Integer. Вы совершенно правы с мнемоником PECS, но это работает только если вы выбрали конкретный тип, который имеет по крайней мере один супер-тип и по крайней мере один подтип.

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