Общий вывод в конструкторах
Если у меня есть класс Foo
:
public class Foo<T> {
public Foo(T t) {
//do something
}
public static <E> void bar(E e) {
//do something
}
}
Почему Foo.bar("String");
делает вывод, что E
является строкой (и поэтому не выдает предупреждение компилятора), но new Foo("String");
не означает, что T
является строкой?
Ответы
Ответ 1
Поскольку конструктор можно рассматривать как специальный метод экземпляра, он не набирается - он получает свой тип от имени класса (с параметром типа), например Foo<String>
. т.е. конструктор не определяется как:
public <T> Foo(T t) ...
и не может быть. Это позволит скрыть общий тип класса (и вы получите предупреждение)
Набирается статический метод. FYI, вызов с несимметричным параметром, как только будет выведен тип, эквивалентен:
Foo.<String>bar("String");
Ответ 2
Когда Java реализовал generics, было решено, что генерируемый класс, созданный без параметров типа, всегда будет возвращать raw-type. Это отличается от общих методов отсутствующими параметрами типа, которые компилятор пытается вывести тип. Из учебников по Java:
Как правило, компилятор Java может вывести параметры типа общего вызова метода. Следовательно, в большинстве случаев вам не нужно указывать их.
Но когда обсуждение переходит к конструкторам:
Обратите внимание, что для использования автоматического вывода типа во время создания экземпляра универсального класса вы должны указать алмаз. В следующем примере компилятор генерирует предупреждение без предупреждения, потому что конструктор HashMap() ссылается на тип HashMap
raw, а не тип Map<String, List<String>>
:
Map<String, List<String>> myMap = new HashMap(); // unchecked
conversion warning
источник: http://download.oracle.com/javase/tutorial/java/generics/gentypeinference.html
Это остается тем же самым в Java 7, однако они пытались сделать его менее повторяющимся, поддерживая синтаксис бриллианта. Например, Foo<String> foo = new Foo<>("String")
. См. этот раздел той же статьи.
Ответ 3
Думаю, вам нужно это сделать
new Foo<String>("String");
сообщить, передают ли данные генериков; аналогично API коллекций.
Ответ 4
Взглянув в это, я собираюсь угадать здесь. Рассмотрим следующее:
public class Foo {
public <E> Foo(E t) {
//do something
}
public static <E> void bar(E e) {
//do something
}
}
В вышеприведенном классе вы не получаете предупреждения при создании Foo
следующим образом:
Foo f = new Foo("String");
Это работает, потому что здесь вызывается тип E
. Так же, как вы ожидаете, что это произойдет в случае метода. Однако ошибка, которую вы получаете, связана не с тем, что тип аргумента не выводится, а потому, что тип raw для класса не может быть выведен.
Я думаю, что это сводится к тому, что тип raw класса может распространяться на методы, но методы не могут установить класс raw типа с использованием вывода.
Ответ 5
Ответ @Kublai Khan правильный; тип new Foo(s)
является raw Foo
, для обратной совместимости.
Вывод типа XML-конструктора Java7 (new Foo<>(s)
) совпадает с термином метода метода методом method.
http://cr.openjdk.java.net/~darcy/ProjectCoin/ProjectCoin-Documentation-v0.9375.html#diamond
Если выражение создания экземпляра класса использует "< > " для исключения аргументов типа класса, список методов m1... mk определяется с целью разрешения перегрузки и вывода аргумента типа...
... затем выбирается один из m1... mk, используя процесс, описанный в п. 15.12.2 (Определение сигнатуры метода)
Ваше подозрение верно, конструкторы могут использовать вывод так же, как и методы, нет существенных различий. Вам просто нужно добавить <>
из-за проблемы с обратной совместимостью.
Почему это потребовало Java 6 лет, чтобы добавить эту функцию - это еще одна история.