Используется для синтаксиса объявления синтаксического типа с явным типом в Java - программирование
Подтвердить что ты не робот

Используется для синтаксиса объявления синтаксического типа с явным типом в Java

Недавно я столкнулся с странным синтаксисом для явного объявления родовых типов при вызове методов Java. Например:

Collections.<String>emptyList();

возвращает пустой List<String>. Однако это кажется глупым, так как реализация <T> emptyList() - это просто непроверенный тип cast (List<T>) EMPTY_LIST, так что все результаты имеют однотипное стирание (и являются одним и тем же объектом). Более того, этот вид объявления явного типа обычно не является потому что компилятор часто выводит типы:

List<String> empty = Collections.emptyList();

После того, как я сделал еще несколько копаний, я нашел два других раза, когда вы захотите использовать этот синтаксис, и все они связаны с использованием библиотеки Guava и , по-видимому, попыткой поставить слишком много операторов в одной строке.

  • Украшение коллекции, например, с помощью синхронизированной обертки, а компилятор не может вывести типы. Следующее не работает, если вы выведете объявление типа: cannot convert from Set<Object> to Set<String>:

    Set<String> set = Collections.synchronizedSet(Sets.<String>newHashSet());
    
  • Получение менее специфических параметров типа, когда компилятор пытается сделать те, которые слишком специфичны. Например, без объявления типа также возникает следующее выражение: cannot convert from Map<String, String> to Map<String, Object>:

    Map<String, Object> toJson = ImmutableMap.<String, Object>of("foo", "bar");
    

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

Однако эту конструкцию языка можно избежать, за исключением тех странных случаев использования, изобретенных командой Guava. Более того, мне кажется, что есть способ для компилятора вывести аргументы типа в обоих примерах, и разработчики просто решили не делать этого. Существуют ли примеры того, что он когда-либо был необходим или полезен для использования этой конструкции в программировании на Java или существует ли она исключительно для упрощения процесса разработки компилятора/JDK?

4b9b3361

Ответ 1

Как "закрытие компилятора" не "необходимо или полезно?" Я считаю это необходимым и полезным для компиляции моего кода.

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

И если вы действительно хотите вникать в сложности вывода типа, он начинается и заканчивается спецификацией языка Java. Вы хотите сосредоточиться на JLS & sect; 15.12.2.7. Вывод аргументов типа на основе фактических аргументов и & sect; 15.12.2.8. Вывод аргументов неразрешенного типа.

Ответ 2

Я нашел по крайней мере один случай, когда компилятор правильно описывает типы, и он по-прежнему необходим: когда вы хотите использовать результат как более общий тип. Возьмите этот метод, который в основном создает List<T> с нуля или более объектов T:

public static <T> List<T> listOf(T... items) {
    ArrayList<T> list = new ArrayList<T>();
    for (T item : items)
        list.add(item);

    return list;
}

Идея состоит в том, что вы можете использовать ее следующим образом:

List<Integer> numbers = ListUtils.listOf(1, 2, 3);

Теперь предположим, что у вас есть метод, который может получить List<Object>:

public static void a(List<Object> objs) {
    ...
}

и что вы хотите предоставить список, построенный с помощью метода listOf():

a(ListUtils.listOf(1, 2, 3));

Это не будет компилироваться, так как тип параметра метода List<Object>, а предоставленный аргумент List<Integer>. В этом случае мы можем изменить вызов на:

a(ListUtils.<Object>listOf(1, 2, 3));

который компилируется, как и ожидалось.

Ответ 3

Вывод типа Java невероятно слаб. Единственный раз, когда нет необходимости включать явный тип в общий метод, такой как emptyList(), - это когда результат метода определяет переменную. Если вы попытаетесь передать пустой список в качестве аргумента другого метода (пример 1), то ситуация, которая возникает для меня ежедневно (и я еще не использую Guava), компилятор полностью отказывается от вывода типа. Я не вижу, как объявление пустого списка в качестве локальной одноразовой переменной - "слишком много операторов в одной строке", как вы ее называете; пустым списком является очень простое подвыражение, за исключением того, что вывод Java-жалкого типа делает его сложным. Сравните с Scala, который будет делать вывод в трех разных ситуациях.