Почему я не могу явно передать аргумент типа универсальному методу Java?

Я определил функцию Java:

static <T> List<T> createEmptyList() {
    return new ArrayList<T>();
}

Один из способов назвать это так:

List<Integer> myList = createEmptyList(); // Compiles

Почему я не могу вызвать его, явно передав аргумент универсального типа? :

Object myObject = createEmtpyList<Integer>(); // Doesn't compile. Why?

Я получаю ошибку Illegal start of expression от компилятора.


person pauldoo    schedule 24.08.2008    source источник
comment
Cheekysoft объясняет почему, но если вы действительно чувствуете необходимость добавить определение второго типа, просто добавьте имя класса перед вызовом статической функции: List‹Integer›myList = MyClass.‹Integer›createEmptyList();   -  person Aaron K    schedule 21.05.2009


Ответы (3)


Когда компилятор java не может сам определить тип параметра для статического метода, вы всегда можете передать его, используя полное имя метода: Class . ‹ Тип > метод();

Object list = Collections.<String> emptyList();
person Community    schedule 25.08.2008
comment
Для нестатических методов вы можете использовать это ключевое слово: Object list = this.<String> emptyList(); - person Roland; 25.10.2014

Вы можете, если вы передадите тип в качестве параметра метода.

static <T> List<T> createEmptyList( Class<T> type ) {
  return new ArrayList<T>();
}

@Test
public void createStringList() {
  List<String> stringList = createEmptyList( String.class );
}

Методы не могут быть универсализированы так же, как тип, поэтому единственный вариант для метода с динамически типизированным универсальным типом возвращаемого значения — фух, как много слов :-) — это передать тип в качестве аргумента.

По-настоящему превосходный FAQ по дженерикам Java можно найти в FAQ Анжелики Лангер по дженерикам.

.
.

Последующие действия:

В этом контексте не имеет смысла использовать аргумент массива, как в Collection.toArray( T[] ). Единственная причина, по которой используется массив, заключается в том, что тот же (предварительно выделенный) массив используется для хранения результатов (если массив достаточно велик, чтобы поместить их все). Это позволяет экономить на постоянном выделении нового массива во время выполнения.

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

static <T> List<T> createEmptyList( T[] array ) {
  return new ArrayList<T>();
}

@Test
public void testThing() {
  List<Integer> integerList = createEmptyList( new Integer[ 1 ] );
}
person Cheekysoft    schedule 24.08.2008
comment
-1 Это распространенное заблуждение, что литерал класса должен быть передан в универсальный метод, когда никакие другие аргументы не позволяют компилятору вывести универсальный тип. Все, что действительно необходимо, — это синтаксис, упомянутый в ответе Ксавьера. В любом случае, как упоминается в ответе Хенрика, нет реального варианта использования для указания параметров типа нового универсального объекта, когда он сразу назначается переменная - с этого момента имеет значение только тип этой переменной, даже во время компиляции. Это причина синтаксиса <> в Java 7. - person Paul Bellora; 30.03.2012
comment
Вы не используете параметр type, поэтому не следует его передавать - person newacct; 07.07.2013
comment
Это даст предупреждение о том, что тип никогда не используется, поэтому лучше позволить автоматическому выводу типа компиляции. - person abhihello123; 05.12.2019

@pauldoo Да, вы совершенно правы. Это одна из слабых сторон java-дженериков imho.

В ответ на Cheekysoft я хотел бы предложить также посмотреть, как это делается самими Java-людьми, такими как T[] AbstractCollection#toArray(T[] a). Я думаю, что версия Cheekysoft лучше, но у версии Java есть преимущество знакомства.

Изменить: добавлена ​​ссылка. Повторное редактирование: нашел ошибку на SO :)


Дополнение к Cheekysoft: Что ж, поскольку это список некоторого типа, который должен быть возвращен, соответствующий пример должен выглядеть примерно так:

static <T> List<T> createEmptyList( List<T> a ) {
  return new ArrayList<T>();
}

Но да, передача объекта класса явно лучше. Мой единственный аргумент — фамильярность, и в данном конкретном случае она ничего не стоит (на самом деле она плоха).

person Henrik Gustafsson    schedule 24.08.2008