"Инициализатор массива нуждается в явном целевом типе" - почему?

Следуя 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) Брайана Гетца:

  1. Почему нельзя использовать 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 } тип внезапно определяется самим инициализатором. Это может привести к (проще создавать) ошибки компилятора или двусмысленности.