Ссылка на конструктор - нет предупреждения при создании массива generics
в Java невозможно создать массив универсального типа напрямую:
Test<String>[] t1 = new Test<String>[10]; // Compile-time error
однако, мы можем сделать это с помощью raw типа:
Test<String>[] t2 = new Test[10]; // Compile warning "unchecked"
в Java 8 также можно использовать ссылку на конструктор:
interface ArrayCreator<T> {
T create(int n);
}
ArrayCreator<Test<String>[]> ac = Test[]::new; // No warning
Test<String>[] t3 = ac.create(10);
почему компилятор не выводит предупреждение в последнем случае? Он все еще использует необработанный тип для создания массива, верно?
3 ответа:
ваш вопрос оправдан. Короче говоря, ссылка на метод действительно использует необработанный тип (или должна использовать необработанный тип), и причина, по которой создание универсальных массивов запрещено, по-прежнему применяется при использовании ссылок на методы, следовательно, возможность молчаливого создания функции создание универсального массива явно нарушает намерение языкового дизайна.
причина, по которой создание универсального массива запрещено, заключается в том, что наследование типа массива, вытекающее из a эра предварительно дженериков, несовместимо с универсальной системы типов. То есть вы можете написать:
IntFunction<List<String>[]> af = List[]::new; // should generate warning List<String>[] array = af.apply(10); Object[] objArray = array; objArray[0] = Arrays.asList(42); List<String> list = array[0]; // heap pollution
в этом месте, следует подчеркнуть, что вопреки некоторым ответам здесь, компилятор не выполнить вывод типа по выражению
List[]::new
чтобы вывести общий тип элементаList<String>
. Легко доказать, что создание универсального массива по-прежнему запрещено:IntFunction<List<String>[]> af = List<String>[]::new; // does not compile
С
List<String>[]::new
незаконно, было бы странно, еслиList[]::new
был принят без a предупреждение, делая вывод, что это фактически незаконноList<String>[]::new
.JLS §15.13 четко указано:
если выражение ссылки на метод имеет вид interfacetype, этот метод всегда
::
new
, потом interfacetype, этот метод всегда должен обозначать тип, который является reifiable (§4.7), или ошибка времени компиляции происходит.это уже означает, что
List<String>[]::new
это незаконно, потому чтоList<String>
не поддается проверке, тогда какList<?>[]::new
is права, какList<?>
это reifiable, иList[]::new
является законным, если мы рассматриваемList
быть сырого типа, как сырого типаList
это reifiable.тогда §15.13.1 гласит:
если выражение ссылки на метод имеет вид interfacetype, этот метод всегда
::
new
, рассматривается один условный метод. Метод имеет один параметр типаint
возвращает interfacetype, этот метод всегда, а неthrows
пункт. Если n = 1, это единственный потенциально применимый метод; в противном случае, нет никаких потенциально применимых методов.другими словами, поведение
List[]::new
выражение Выше такое же, как если бы вы написали:IntFunction<List<String>[]> af = MyClass::create; … private static List[] create(int i) { return new List[i]; }
разве что метод
create
- это только условная. И действительно, с этим явным объявлением метода есть только сырого типа предупреждения наcreate
метод, но нет unchecked предупреждения относительно преобразованияList[]
доList<String>[]
при ссылке на метод. Так что понятно, что происходит в компиляторе вList[]::new
случай, когда метод, использующий необработанные типы, является только условным, т. е. не существует в исходном коде.но отсутствие unchecked предупреждения-это явное нарушение JLS §5.1.9, непроверенное преобразование:
пусть
G
имя a объявление универсального типа с n параметры типа.есть непроверенное преобразование из класса raw или типа интерфейса (§4.8)
G
к любому параметризованному типу формыG<T₁,...,T>
.существует непроверенное преобразование из необработанного типа массива
G[]ᵏ
к любому типу массива в видеG<T₁,...,T>[]ᵏ
. (Обозначение[]ᵏ
указывает тип массива k габариты.)использование непроверенное преобразование вызывает время компиляции предупреждение непроверенное если все аргументы типа
T
ᵢ (1 ≤ я≤ n) являются неограниченными подстановочными знаками (§4.5.1), или непроверенное предупреждение подавляетсяSuppressWarnings
аннотация (§9.6.4.5).Итак, преобразование
List[]
доList<?>[]
законно, какList
параметризуется с неограниченным подстановочным знаком, но преобразование изList[]
доList<String>[]
должны производить unchecked предупреждение, которое имеет решающее значение здесь, как использованиеList[]::new
не производят сырого типа предупреждение, которое появляется с явным методом создания. Отсутствие сырого типа предупреждения, кажется, не является нарушением (насколько я понял §4.8) и это не будет проблемой, еслиjavac
создали unchecked предупреждение.
лучшее, что я могу придумать, это то, что JLS указывает, что ссылка метода на конструктор универсального типа выводит общие параметры: "Если метод или конструктор является универсальным, соответствующие аргументы типа могут быть либо выведены, либо предоставлены явно."Позже это дает
ArrayList::new
в качестве примера и описывает его как "предполагаемые аргументы типа для универсального класса", тем самым установив, чтоArrayList::new
(а неArrayList<>::new
) синтаксис, который выводит аргументы.дан класс:
public static class Test<T> { public Test() {} }
это дает предупреждение:
Test<String> = new Test(); // No <String>
но это не так:
Supplier<Test<String>> = Test::new; // No <String> but no warning
, потому что
Test::new
неявно выводит типовые аргументы.поэтому я предполагаю, что ссылка метода на конструктор массива работает таким же образом.
Он по-прежнему использует тип raw для создания массива, верно?
Java generics - это просто иллюзия времени компиляции, поэтому необработанный тип, конечно же, будет использоваться во время выполнения для создания массива.
Почему компилятор не выводит предупреждение в последнем случае?
да, непроверенный бросок от
Test[]
toTest<String>[]
все еще происходит; это просто происходит за кулисами в анонимном контекст.Test<String>[] t3 = ((IntFunction<Test<String>[]>) Test[]::new).apply(10);
С анонимный метод делает грязную работу, непроверенное приведение эффективно исчезает из управляемого кода.