Что означают аргументы типа конструктора при размещении * перед * типом? - PullRequest
124 голосов
/ 25 марта 2019

Я недавно столкнулся с этим необычным (для меня) синтаксисом Java ... вот пример этого:

List list = new <String, Long>ArrayList();

Обратите внимание на расположение аргументов типа <String, Long> ... это не такпосле типа, как обычно, но раньше.Я не против признать, что никогда раньше не видел этот синтаксис.Также обратите внимание, что есть 2 аргумента типа, когда ArrayList имеет только 1.

Имеет ли расположение аргументов типа то же значение, что и размещение их после типа?Если нет, что означает другое позиционирование?

Почему допустимо иметь аргументы 2 типов, когда ArrayList имеет только 1?

Я искал обычные места, например.Анжелика Лангер и здесь, но не может найти никакого упоминания об этом синтаксисе нигде, кроме правил грамматики в файле грамматики Java в проекте ANTLR.

Ответы [ 2 ]

138 голосов
/ 25 марта 2019

Вызов универсального конструктора

Это необычно, но вполне допустимая Java.Чтобы понять, нам нужно знать, что класс может иметь универсальный конструктор, например:

public class TypeWithGenericConstructor {

    public <T> TypeWithGenericConstructor(T arg) {
        // TODO Auto-generated constructor stub
    }

}

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

    new TypeWithGenericConstructor(LocalDate.now(ZoneId.systemDefault()));

Теперь T явно LocalDate.Однако могут быть случаи, когда Java не может вывести (вывести) аргумент типа.Затем мы предоставляем его явно, используя синтаксис из вашего вопроса:

    new <LocalDate>TypeWithGenericConstructor(null);

Конечно, мы можем также предоставить его, даже если в этом нет необходимости, если мы считаем, что это помогает удобочитаемости или по какой-либо причине:

    new <LocalDate>TypeWithGenericConstructor(LocalDate.now(ZoneId.systemDefault()));

В вашем вопросе вы, кажется, вызываете конструктор java.util.ArrayList.Этот конструктор не является универсальным (только класс ArrayList в целом, это нечто другое).Почему Java позволяет вам предоставлять аргументы типа в вызове, когда они не используются, см. Мое редактирование ниже.Мой Eclipse выдает мне предупреждение :

Неиспользуемые аргументы типа для неуниверсального конструктора ArrayList () типа ArrayList;он не должен быть параметризован с аргументами

Но это не ошибка, и программа работает нормально (я дополнительно получаю предупреждения об отсутствующих аргументах типа для List и ArrayList, но это опять-такидругая история).

Универсальный класс по сравнению с универсальным конструктором

Имеет ли расположение аргументов типа то же значение, что и размещение их после типа?Если нет, что означает другое позиционирование?

Нет, это другое.Обычный аргумент типа / s после типа (ArrayList<Integer>()) предназначен для универсального класса .Аргументы типа перед предназначены для конструктора .

Также можно комбинировать две формы:

    List<Integer> list = new <String, Long>ArrayList<Integer>();

Я бы немного об этом подумалболее правильно, поскольку теперь мы можем видеть, что в списке хранится Integer объектов (я бы, конечно, предпочел бы пропустить бессмысленные <String, Long>).

Почему законно иметь 2Аргументы типа, когда ArrayList имеет только 1?

Во-первых, если вы передаете аргументы типа перед типом, вы должны указать правильный номер для конструктора, а не для класса, чтобы он ничего не получилделать с тем, сколько аргументов типа есть у класса ArrayList.Это действительно означает, что в этом случае вы не должны указывать ничего, так как конструктор не принимает аргументы типа (он не является универсальным).Когда вы в любом случае предоставляете некоторые из них, они игнорируются, поэтому не имеет значения, сколько или сколько вы предоставляете.

Почему допустимы бессмысленные аргументы типа?

Редактируйте с помощью @Slaw для ссылки: Java допускает аргументы типа для всех вызовов методов.Если вызываемый метод является универсальным, используются аргументы типа;если нет, они игнорируются.Например:

    int length = "My string".<List>length();

Да, это абсурд.Спецификация языка Java (JLS) дает это обоснование в подразделе 15.12.2.1:

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

Аргумент не выполняется для конструкторов, поскольку их нельзя напрямую переопределить.Но я полагаю, что они хотели иметь одно и то же правило, чтобы не усложнять и без того сложные правила.В любом случае, раздел 15.9.3 об экземплярах и new более одного раза ссылается на 15.12.2.

Ссылки

0 голосов
/ 25 марта 2019

Очевидно, что вы можете добавить любой неуниверсальный метод / конструктор к любому универсальному параметру, который вам нравится:

new <Long>String();
Thread.currentThread().<Long>getName();

Компилятору все равно, потому что он не должен сопоставлять аргументы типа theses с фактическимиуниверсальные параметры.

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

Collections.<String, Long>singleton("A"); // does not compile

Для меня это похоже на ошибку компилятора.

...