Java: ограниченные подстановочные знаки или параметр ограниченного типа?

Недавно я прочитал эту статью: http://download.oracle.com/javase/tutorial/extra/generics/wildcards.html

Мой вопрос заключается не в создании такого метода:

public void drawAll(List shapes){ for (Shape s: shapes) { s.draw(this); } } 

Я могу создать такой метод, и он отлично работает:

 public  void drawAll(List shapes){ for (Shape s: shapes) { s.draw(this); } } 

Каким образом я должен использовать? В этом случае полезно использовать подстановочные знаки?

Это зависит от того, что вам нужно сделать. Вам нужно использовать параметр ограниченного типа, если вы хотите сделать что-то вроде этого:

 public  void addIfPretty(List shapes, T shape) { if (shape.isPretty()) { shapes.add(shape); } } 

Здесь у нас есть формы List shapes и T shape , поэтому мы можем смело shapes.add(shape) . Если он был объявлен List List , вы НЕ можете безопасно add к нему (потому что у вас может быть List и Circle ).

Поэтому, указав имя параметру ограниченного типа, мы можем использовать его в другом месте в нашем общем методе. Разумеется, эта информация не всегда требуется, поэтому, если вам не нужно много знать о типе (например, drawAll ), тогда достаточно всего лишь подстановочного знака.

Даже если вы снова не ссылаетесь на параметр ограниченного типа, параметр ограниченного типа по-прежнему требуется, если у вас есть несколько ограничений. Вот цитата из часто задаваемых вопросов Java Generics от Angelika Langer

В чем разница между привязкой шаблона и привязкой параметра типа?

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

Ограничения подстановочных знаков и ограничения параметров типа часто сбиты с толку, потому что они оба называются границами и имеют частично похожий синтаксис. […]

Синтаксис :

  type parameter bound T extends Class & Interface1 & … & InterfaceN wildcard bound upper bound ? extends SuperType lower bound ? super SubType 

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

Параметр типа в constrast может иметь несколько границ, но нет такой вещи, как нижняя граница для параметра типа.

Цитаты из Effective Java 2nd Edition, Item 28: Используйте ограниченные подстановочные знаки для повышения гибкости API :

Для максимальной гибкости используйте типы подстановок на входных параметрах, которые представляют производителей или потребителей. […] PECS выступает за продюсеров-продюсеров, потребитель- super […]

Не используйте типы подстановочных знаков в качестве возвращаемых типов . Вместо того, чтобы предоставлять дополнительную гибкость для ваших пользователей, это заставит их использовать подстановочные типы в клиентском коде. Правильно используемые типы подстановочных знаков почти невидимы для пользователей classа. Они вызывают методы принятия параметров, которые они должны принимать, и отвергают те, которые они должны отклонить. Если пользователь classа должен думать о типах подстановочных знаков, возможно, что-то не так с API classа .

Применяя принцип PECS, мы теперь можем вернуться к нашему примеру addIfPretty и сделать его более гибким, написав следующее:

 public  void addIfPretty(List list, T shape) { … } 

Теперь мы можем addIfPretty , скажем, Circle , в List . Это явно типично, но наша оригинальная декларация не была достаточно гибкой, чтобы позволить ей.

Связанные вопросы

  • Java Generics: Что такое PECS?
  • Может кто-нибудь объяснить, что делает и когда он должен использоваться и как эта конструкция должна сотрудничать с и ?

Резюме

  • Используйте параметры ограниченного типа / подстановочные знаки, они повышают гибкость вашего API
  • Если тип требует нескольких параметров, у вас нет выбора, кроме как использовать параметр ограниченного типа
  • если тип требует нижней границы, у вас нет выбора, кроме как использовать ограниченный шаблон
  • «Продюсеры» имеют верхние границы, «потребители» имеют нижние границы
  • Не используйте подстановочный знак в обратных типах

В вашем примере вам не нужно использовать T, так как вы не используете этот тип где-либо еще.

Но если вы сделали что-то вроде:

 public  T drawFirstAndReturnIt(List shapes){ T s = shapes.get(0); s.draw(this); return s; } 

или как упомянутые полигенные смазки, если вы хотите совместить параметр типа в списке с другим параметром типа:

 public  void mergeThenDraw(List shapes1, List shapes2) { List mergedList = new ArrayList(); mergedList.addAll(shapes1); mergedList.addAll(shapes2); for (Shape s: mergedList) { s.draw(this); } } 

В первом примере вы получаете немного больше безопасности типов, а затем возвращаете только Shape, так как вы можете передать результат функции, которая может занять дочерний элемент Shape. Например, вы можете передать List в мой метод, а затем передать полученный квадрат методу, который принимает только квадраты. Если вы использовали ‘?’ вам нужно было бы отобразить полученную форму Shape Square, которая не была бы безопасным по типу.

Во втором примере вы гарантируете, что оба списка имеют один и тот же параметр типа (который вы не можете сделать с??), Так как каждый «?» Отличается), так что вы можете создать список, содержащий все элементы из обоих из них ,

Рассмотрим следующий пример из «Программирование на Java» Джеймса Гослинга 4-го издания ниже, где мы хотим объединить 2 SinglyLinkQueue:

 public static  void merge(SinglyLinkQueue d, SinglyLinkQueue s){ // merge s element into d } public static  void merge(SinglyLinkQueue d, SinglyLinkQueue s){ // merge s element into d } 

Оба вышеуказанных метода имеют одинаковую функциональность. Итак, что предпочтительнее? Ответ второй. В собственных словах автора:

«Общее правило заключается в использовании подстановочных знаков, когда вы можете, потому что код с подстановочными знаками, как правило, более читабельен, чем код с несколькими параметрами типа. Если вы решите, нужна ли вам переменная типа, спросите себя, используется ли эта переменная типа для связывания двух или более параметров, или связать тип параметра с типом возвращаемого значения. Если ответ отрицательный, тогда необходимо иметь подстановочный знак. “

Примечание. В книге указывается только второй метод и имя параметра типа S вместо «T». Первый метод не существует в книге.

Насколько я понимаю, подстановочный знак позволяет использовать более сжатый код в ситуациях, когда параметр типа не требуется (например, потому, что он упоминается в нескольких местах или потому, что требуется несколько ограничений, как указано в других ответах).

В ссылке вы указываете, что я прочитал (в разделе «Общие методы») следующие утверждения, которые намекают в этом направлении:

Общие методы позволяют использовать параметры типа для выражения зависимостей между типами одного или нескольких аргументов метода и / или его возвращаемого типа. Если такой зависимости нет, общий метод не должен использоваться.

[…]

Использование подстановочных знаков является более четким и более кратким, чем объявление явных параметров типа, и поэтому должно быть предпочтительным, когда это возможно.

[…]

Подстановочные знаки также имеют то преимущество, что их можно использовать вне сигнатур методов, как типы полей, локальные переменные и массивы.

Второй способ немного более подробный, но он позволяет вам ссылаться на T внутри него:

 for (T shape : shapes) { ... } 

Насколько я понимаю, это единственное различие.

Interesting Posts

Как настроить разрешение экрана, сообщаемое в приложение JavaScript веб-браузером?

Поддержка локальной базы данных Sql для телефона Windows 7

Рассчитать компас, несущий / заголовок для местоположения в Android

Возможно ли, чтобы ПК одновременно использовал два сетевых соединения?

Разрешение экрана, отображающее только 800×600 в Xubuntu

Какие кодеки наиболее подходят для воспроизведения с помощью Windows Media Player в Windows XP?

Метод передачи в качестве параметра с использованием C #

Храните PDF в течение ограниченного времени на сервере приложений и делайте его доступным для загрузки

Лучший способ показать индикатор загрузки / прогресса?

Как использовать UrlFetchApp с учетными данными? Скрипты Google

Как отключить интеллектуальный отступ в emacs (и заставить пробелы или вкладки)?

Элементы ListView не доступны для кликов. Зачем?

Установка старой версии пакета R

Удалите повторяющиеся строки в excel, если не все столбцы одинаковы

Что означает в C #

Давайте будем гением компьютера.