Какой смысл в бриллиантовом операторе (<>) в Java 7? - PullRequest
435 голосов
/ 12 ноября 2010

Алмазный оператор в Java 7 позволяет код, подобный следующему:

List<String> list = new LinkedList<>();

Однако в Java 5/6 я могу просто написать:

List<String> list = new LinkedList();

Я понимаю, что стирание типов это то же самое. (Универсальный в любом случае удаляется во время выполнения).

Зачем вообще беспокоиться о бриллианте? Какие новые функциональные возможности / безопасность типов это позволяет? Если это не дает никакой новой функциональности, почему они упоминают это как функцию? Мое понимание этой концепции неверно?

Ответы [ 7 ]

480 голосов
/ 12 ноября 2010

Проблема с

List<String> list = new LinkedList();

заключается в том, что с левой стороны вы используете универсальный тип List<String>, а с правой стороны вы используете raw type LinkedList.Необработанные типы в Java эффективно существуют только для совместимости с пре-родовым кодом и никогда не должны использоваться в новом коде, если только вам это не нужно.

Теперь, если у Java были дженерики с самого начала и не было типов,такие как LinkedList, которые были изначально созданы до того, как у него были универсальные шаблоны, возможно, он мог бы сделать это так, чтобы конструктор универсального типа автоматически выводил параметры своего типа из левой части присваивания, если это возможно.Но это не так, и он должен обрабатывать необработанные типы и универсальные типы по-разному для обратной совместимости.Это оставляет им необходимость создавать немного другой , но одинаково удобный способ объявления нового экземпляра универсального объекта без необходимости повторять параметры его типа ... оператор diamond.

Что касается вашего исходного примера List<String> list = new LinkedList(), компилятор генерирует предупреждение для этого назначения, потому что это должно.Подумайте об этом:

List<String> strings = ... // some list that contains some strings

// Totally legal since you used the raw type and lost all type checking!
List<Integer> integers = new LinkedList(strings);

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

// Not legal since the right side is actually generic!
List<Integer> integers = new LinkedList<>(strings);

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

Я думаю, что ключевым моментом для понимания является то, что исходные типы (без <>)нельзя рассматривать так же, как универсальные типы.Когда вы объявляете необработанный тип, вы не получаете ни одного из преимуществ и проверки типа обобщений.Вы также должны иметь в виду, что универсальные элементы являются частью общего назначения языка Java ... они не просто применяются к конструкторам без аргументов Collection s!

34 голосов
/ 12 ноября 2010

Ваше понимание слегка ошибочно. Оператор с бриллиантами - хорошая функция, так как вам не нужно повторяться. Имеет смысл определять тип один раз, когда вы объявляете тип, но просто не имеет смысла определять его снова с правой стороны. Принцип СУХОГО.

Теперь объясним все, что связано с определением типов. Вы правы в том, что тип удаляется во время выполнения, но как только вы захотите извлечь что-то из списка с определением типа, вы получите его обратно как тип, который вы определили при объявлении списка, иначе он потеряет все специфические функции и будет иметь только Особенности объекта, за исключением случаев, когда вы приводите извлеченный объект к его исходному типу, который иногда может быть очень сложным и привести к исключению ClassCastException.

Используя List<String> list = new LinkedList(), вы получите предупреждения rawtype.

16 голосов
/ 12 ноября 2010

Эта строка вызывает предупреждение [unchecked]:

List<String> list = new LinkedList();

Итак, вопрос трансформируется: почему предупреждение [unchecked] не подавляется автоматически только для случая, когда создается новая коллекция?

Я думаю, это было бы гораздо более сложной задачей, чем добавление функции <>.

UPD : Я также думаю, что был бы беспорядок, если бы по закону использовались необработанные типы «только для нескольких вещей».

13 голосов
/ 19 января 2011

Теоретически, оператор diamond позволяет писать более компактный (и читаемый) код, сохраняя аргументы повторяющегося типа.На практике это только два запутанных символа, которые больше ничего не дают.Почему?

  1. Ни один здравомыслящий программист не использует необработанные типы в новом коде.Таким образом, компилятор может просто предположить, что, не записывая аргументы типа, вы хотите, чтобы он выводил их.
  2. Оператор diamond не предоставляет информации о типе, он просто говорит компилятору: «Все будет хорошо».Таким образом, опуская его, вы не можете причинить вреда.В любом месте, где оператор Diamond допустим, он может быть «выведен» компилятором.

ИМХО, наличие ясного и простого способа пометить источник как Java 7 было бы более полезным, чем придумыватьстранные вещи.В таком маркированном коде необработанные типы могут быть запрещены без потери чего-либо.

Кстати, я не думаю, что это должно быть сделано с помощью переключателя компиляции.Java-версия программного файла является атрибутом файла, никакой опции вообще нет.Использование чего-то столь же тривиального, как

package 7 com.example;

, может прояснить это (вы можете предпочесть что-то более сложное, включая одно или несколько необычных ключевых слов).Это даже позволило бы без проблем скомпилировать исходники, написанные для разных версий Java.Это позволило бы вводить новые ключевые слова (например, «модуль») или отбрасывать некоторые устаревшие функции (несколько непубличных, не вложенных классов в одном файле или вообще) без потери какой-либо совместимости.

8 голосов
/ 12 ноября 2010

Когда вы пишете List<String> list = new LinkedList();, компилятор выдает «непроверенное» предупреждение. Вы можете игнорировать его, но если вы раньше игнорировали эти предупреждения, вы также можете пропустить предупреждение, уведомляющее вас о реальной проблеме безопасности типа.

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

4 голосов
/ 04 января 2012

Все сказанные в других ответах действительны, но варианты использования не полностью действительны ИМХО.Если проверить Guava и особенно вещи, связанные с коллекциями, то же самое было сделано со статическими методами.Например, Lists.newArrayList () , который позволяет писать

List<String> names = Lists.newArrayList();

или со статическим импортом

import static com.google.common.collect.Lists.*;
...
List<String> names = newArrayList();
List<String> names = newArrayList("one", "two", "three");

Гуава имеет и другие очень мощные функции, подобные этой, и я на самом делене могу придумать много вариантов использования для <>.

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

3 голосов
/ 12 ноября 2010

Смысл для оператора diamond заключается в том, чтобы просто уменьшить число типов кода при объявлении универсальных типов.Это никак не влияет на время выполнения.

Единственное отличие, если вы укажете в Java 5 и 6

List<String> list = new ArrayList();

, заключается в том, что вы должны указать @SuppressWarnings("unchecked") для list (в противном случае вы получите непроверенное предупреждение о сотворении).Насколько я понимаю, оператор бриллиантов пытается облегчить разработку.Он не имеет никакого отношения к выполнению генериков во время выполнения вообще.

...