В чем смысл бриллиантового оператора в Java 7?
Алмазный оператор в Java 7 позволяет код, подобный следующему:
List<String> list = new LinkedList<>();
Однако в Java 5/6 я могу просто написать:
List<String> list = new LinkedList();
Я понимаю, что стирание типов это то же самое. (Универсальный в любом случае удаляется во время выполнения).
Зачем вообще беспокоиться о бриллианте? Какие новые функциональные возможности / безопасность типов это позволяет? Если это не дает никакой новой функциональности, почему они упоминают это как функцию? Мое понимание этой концепции неверно?
7 ответов
Вопрос с
List<String> list = new LinkedList();
является то, что на левой стороне, вы используете универсальный тип List<String>
где на правой стороне вы используете необработанный тип 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!
Ваше понимание слегка ошибочно. Оператор с бриллиантами - отличная возможность, так как вам не нужно повторяться. Имеет смысл определять тип один раз, когда вы объявляете тип, но просто не имеет смысла определять его снова с правой стороны. СУХОЙ принцип.
Теперь объясним весь паз об определении типов. Вы правы в том, что тип удаляется во время выполнения, но как только вы захотите извлечь что-то из списка с определением типа, вы получите его обратно как тип, который вы определили при объявлении списка, иначе он потеряет все специфические функции и будет иметь только Возможности объекта, за исключением случаев, когда вы приводите извлеченный объект к его исходному типу, который иногда может быть очень сложным и приводить к исключению ClassCastException.
С помощью List<String> list = new LinkedList()
вы получите необработанные предупреждения.
Эта строка вызывает предупреждение [unchecked]:
List<String> list = new LinkedList();
Итак, вопрос трансформируется: почему предупреждение [unchecked] не подавляется автоматически только для случая, когда создается новая коллекция?
Я думаю, это было бы гораздо более сложной задачей, чем добавление <>
особенность.
UPD: Я также думаю, что был бы беспорядок, если бы юридически использовались необработанные типы "только для нескольких вещей".
Теоретически, оператор diamond позволяет писать более компактный (и читаемый) код, сохраняя аргументы повторного типа. На практике это только два запутанных символа, которые больше ничего не дают. Зачем?
- Ни один здравомыслящий программист не использует необработанные типы в новом коде. Таким образом, компилятор может просто предположить, что, не записывая аргументы типа, вы хотите, чтобы он выводил их.
- Оператор diamond не предоставляет никакой информации о типе, он просто говорит компилятору: "Все будет хорошо". Таким образом, опуская его, вы не можете причинить вреда. В любом месте, где бриллиантовый оператор допустим, он может быть "выведен" компилятором.
ИМХО, наличие ясного и простого способа пометить источник как Java 7 было бы более полезным, чем придумывать такие странные вещи. В таком помеченном коде необработанные типы могут быть запрещены, не теряя ничего.
Кстати, я не думаю, что это должно быть сделано с помощью переключателя компиляции. Java-версия программного файла является атрибутом файла, никакой опции вообще нет. Используя что-то тривиальное, как
package 7 com.example;
может прояснить это (вы можете предпочесть что-то более сложное, включая одно или несколько необычных ключевых слов) Это даже позволило бы без проблем скомпилировать исходники, написанные для разных версий Java. Это позволило бы вводить новые ключевые слова (например, "модуль") или отбрасывать некоторые устаревшие функции (несколько непубличных, не вложенных классов в одном файле или вообще) без потери какой-либо совместимости.
Когда ты пишешь List<String> list = new LinkedList();
Компилятор выдает "непроверенное" предупреждение. Вы можете игнорировать это, но если вы раньше игнорировали эти предупреждения, вы также можете пропустить предупреждение, уведомляющее вас о реальной проблеме безопасности типа.
Итак, лучше написать код, который не генерирует лишних предупреждений, а оператор Diamond позволяет делать это удобным способом без лишних повторов.
Все сказанное в других ответах является действительным, но варианты использования не являются полностью действительными ИМХО. Если кто-то проверяет 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.
Смысл для оператора diamond - просто уменьшить количество типов кода при объявлении универсальных типов. Это никак не влияет на время выполнения.
Единственное отличие, если вы укажете в Java 5 и 6,
List<String> list = new ArrayList();
это то, что вы должны указать @SuppressWarnings("unchecked")
к list
(иначе вы получите непроверенное предупреждение) Насколько я понимаю, оператор бриллиантов пытается облегчить разработку. Он не имеет ничего общего с исполнением обобщений во время выполнения.