"Инициализатор массива нуждается в явном целевом типе" - почему?
Следуя JEP 286: Описание вывода типа Local-Variable
Мне интересно, в чем причина введения такого ограничения, как:
Main.java:199: error: cannot infer type for local variable k
var k = { 1 , 2 };
^
(array initializer needs an explicit target-type)
Поэтому для меня логично это должно быть:
var k = {1, 2}; // Infers int[]
var l = {1, 2L, 3}; // Infers long[]
Поскольку компилятор Java уже может правильно определить тип массива:
void decide() {
arr(1, 2, 3); // call void arr(int ...arr)
arr(1, 2L, 3); // call void arr(long ...arr)
}
void arr(int ...arr) {
}
void arr(long ...arr) {
}
Так в чем же помеха?
Ответы
Ответ 1
Каждый раз, когда мы улучшаем охват вывода типа в Java, мы получаем поток "но вы также можете сделать вывод об этом, почему бы и нет?" (Или иногда, менее вежливо.)
Некоторые общие замечания по схемам проектирования типов:
- Схемы вывода всегда будут иметь ограничения; всегда есть случаи на полях, где мы не можем вывести ответ, или в конечном итоге вывести что-то удивительное. Чем сложнее мы пытаемся сделать все, тем более вероятно, что мы выведем удивительные вещи. Это не всегда лучший компромисс.
- Это легко сделать для вишневых примеров "но, конечно, вы можете сделать вывод в этом случае". Но если такие случаи очень похожи на другие случаи, которые не имеют очевидного ответа, мы только что переместили проблему - "почему она работает для X, но не Y, где X и Y оба Z?"
- Схема выводов всегда может быть использована для обработки инкрементных случаев, но почти всегда есть побочный ущерб, либо в виде получения худшего результата в других случаях, повышенной нестабильности (где, казалось бы, не связанные изменения могут изменить предполагаемый тип), либо более сложная, Вы не хотите оптимизировать только количество дел, которые вы можете сделать; вы также хотите оптимизировать для образованного пользователя способность прогнозировать, что будет работать, а что - нет. Нарисовать более простые строки (например, не стоит пытаться вывести тип инициализаторов массива), часто является победой здесь.
- Учитывая, что всегда существуют ограничения, часто лучше выбирать меньшую, но лучше определенную цель, поскольку это упрощает модель пользователя. (См. Связанные вопросы "Почему я не могу использовать вывод типа для типа возвращаемых методов? Ответ на этот вопрос мы могли бы сделать, но результатом будет более сложная модель пользователя для небольшой выразительной выгоды. плохой возврат-на-сложности. ")
Ответ 2
Из списка рассылки - jep-discuss, сообщение Mail Mail Mail за четверг (чт 10 марта 15:07:54 UTC 2016) Брайана Гетца:
-
Почему нельзя использовать var, когда инициализатор является инициализатором массива, как в:
var ints = { 1, 2, 3 }
Правило таково: мы выводим тип переменной, рассматривая инициализатор как автономное выражение и выводим его тип. Однако инициализаторы массивов, такие как lambdas и ref ref, представляют собой поли выражения - для вычисления их типа необходим целевой тип. Поэтому они отвергаются.
Можем ли мы сделать эту работу? Мы, наверное, могли бы. Но это добавило бы большую сложность в эту функцию, в пользу, в основном, краеугольного случая. Мы хотели бы, чтобы это была простая функция.
Инициализатор коротких рук принимает информацию о типе из декларации, но поскольку объявление здесь var
оно должно быть указано явно.
Вам нужно будет выбрать между:
var k = new int[]{ 1 , 2 };
или же
int[] k = { 1 , 2 };
Разрешение var k = { 1, 2 }
изменило бы семантику того, что уже является синтаксическим сахаром. В случае int[] n = { 1, 2 }
тип определяется декларацией. Если вы разрешите var n = { 1, 2 }
тип внезапно определяется самим инициализатором. Это может привести к (проще создавать) ошибки компилятора или двусмысленности.