Почему оператор ромба используется для вывода типа в Java 7?
List<String> list = new ArrayList();
приведет к предупреждению компилятора.
Однако следующий пример компилируется без предупреждения: List<String> list = new ArrayList<>();
Мне любопытно, зачем вообще нужен ввод алмазного оператора. Почему бы просто не сделать вывод типа в конструкторе, если аргумент типа отсутствует (как это уже было сделано для статических методов в java и использовалось библиотеками коллекций, такими как google guava)
РЕДАКТИРОВАТЬ: Используя миллимус ответ в качестве отправной точки, я посмотрел, что на самом деле стирание типов, и это не просто удаление всей информации о типах. Компилятор на самом деле делает немного больше (скопировано из официального документа):
- Замените все параметры типа в универсальных типах их границами или Object, если параметры типа не ограничены. Таким образом, полученный байт-код содержит только обычные классы, интерфейсы и методы.
- Вставьте слепки типа, если необходимо, чтобы сохранить безопасность типов.
- Генерация мостовых методов для сохранения полиморфизма в расширенных универсальных типах.
6 ответов
Окончательный ответ должен был прийти от того, кто разработал эту функцию, но я предполагаю, что она должна отличать это от использования необработанных типов, которые заставляют компилятор делать что-то совсем другое для совместимости. Выражение с необработанным типом в нем обрабатывается несколько иначе, чем с использованием обобщений, пример можно найти в этом вопросе SO: Общий сбивает несвязанную коллекцию
Разработчики Java очень стараются избежать изменения поведения существующих программ. List<String> list = new ArrayList();
компилирует и создает необработанный ArrayList. Если бы к нему применялся вывод типа, результатом было бы ArrayList<String>
, изменяя его поведение и, возможно, вызывая ошибки времени выполнения в других местах программы.
================================================== ==========================
После дальнейшего рассмотрения и комментария @millimoose я вижу, что изменения в поведении будут локальными для инициализатора и обнаруживаться во время компиляции. Рассмотрим следующую программу:
import java.util.ArrayList;
import java.util.List;
public class Test {
public static void main(String[] args) throws Exception {
List<Integer> integers = new ArrayList<Integer>();
integers.add(Integer.valueOf(3));
integers.add(Integer.valueOf(4));
List<String> list = new ArrayList(integers);
System.out.println(list);
}
}
Без вывода типа, он работает и печатает [3, 4]
несмотря на нежелательную ситуацию List<String>
который содержит целочисленные ссылки.
С выводом типа он не будет компилироваться, потому что ArrayList(Collection<? extends E> c)
не позволяет использовать List<Integer>
в качестве аргумента при создании ArrayList<String>
,
Полный синтаксис, требуемый компилятором Java 5 и 6:
List<String> list = new ArrayList<String>();
Они решили упростить для нас синтаксис и позволить не записывать одинаковые параметры типа по обе стороны от оператора присваивания. Тем не менее <>
Оператор по-прежнему обязан убедиться, что вы понимаете, что вы делаете. Письменно new ArrayList<>()
Вы говорите: "Я понимаю, что я создаю экземпляр универсального типа, а универсальный параметр такой же, какой я объявил в левой части назначения".
Это является частью улучшения Java Generics в Java 7.
Прежде чем вы должны были написать
final List<String> list = new ArrayList<String>();
Теперь вы можете написать
final List<String> list = new ArrayList<>();
Что эквивалентно - компилятор решит это за вас. Это не то же самое, что
final List list = new ArrayList();
Какой нетипизированный List
,
Интересны случаи, когда вызов конструктора с помощью diamond и в виде rawtype успешно компилируется, но создает другой код. Такие примеры возможны при смешивании с функцией перегрузки метода. IIRC, где-то в списке рассылки монет OpenJDK был пример (нет, я не буду пытаться его найти).
Было недопустимо, чтобы на Java SE 6 и Java SE 7 был успешно установлен точно такой же код, но были получены разные результаты.
За мои деньги я бы опустил бриллиант и получил бы предупреждение (считалось ошибкой), если бы алгоритм, определяемый в 7, генерировал бы другой код (по сути, такой же, как и для вывода универсального типа метода из J2SE 5.0). Если вы написали такой код, вероятно, неясно, будет ли он компилируемым или нет.
Если ваш проект построен на Maven, добавьте ниже в pom.xml под тегом. Работает отлично.. <plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.7.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>