Я пытался отсканировать JEP-286 о локальном выводе типа. Я вижу, что это работает только для локальных переменных - понял. Так что это действительно работает:

public class TestClass {
    public static void main(String [] args){
        var list = new ArrayList<>();
        list.add("1");
        System.out.println(list.get(0)); // 1
    }  
}

Я вижу, что это с другой стороны не компилируется:

public class TestClass {
    public var list = new ArrayList<>();
    public static void main(String [] args){

    }
}

Очевидно, что это не так, так как JEP так говорит. Теперь мой вопрос:

Это имеет смысл, чтобы член public / protected , объявленный как var, потерпел неудачу, по крайней мере, IMO. Но почему он не компилируется, даже если это private? Я могу только предположить, что вы все еще можете получить эту переменную с помощью отражения (а я не могу получить локальные поля, подобные этой) ... И получение этой переменной потребует приведения, ну, возможно, очень запутанное приведение.

8
Eugene 2 Мар 2018 в 16:46

5 ответов

Лучший ответ

Мотивация запрета вывода типов для полей и возвратов методов заключается в том, что API должны быть стабильными; доступ к полю и вызов метода связываются дескриптором во время выполнения, поэтому вещи, которые вызывают тонкие изменения в предполагаемых типах, могут привести к поломке существующих скомпилированных клиентов, если изменение в реализации привело к изменению выведенного типа (по модулю стирания.) это для реализации, но не для API, является разумным руководящим принципом.

Резонно спросить: «А как же частные поля и методы?» И действительно, мы вполне могли бы сделать это. Как и все дизайнерские решения, это компромисс; это позволило бы использовать вывод в большем количестве мест в обмен на большую сложность пользовательской модели. (Меня не очень заботит сложность спецификации или компилятора; это наша проблема.) Легче рассуждать о «выводе локальных переменных да, полей и методов нет», чем добавлении различных эпициклических соображений, таких как «но, поля». и методы в порядке, если они являются частными ". Проведение линии, в которой мы это сделали, также означает, что последствия совместимости, связанные с изменением поля или метода с частного на частное, не имеют случайных взаимодействий с логическим выводом.

Таким образом, краткий ответ заключается в том, что, делая это таким образом, вы упрощаете язык, не делая эту функцию значительно менее полезной.

16
Brian Goetz 2 Мар 2018 в 20:59

Различные причины:

  1. Видимость и тип являются ортогональными - одно не должно влиять на другое. Если закрытые переменные можно инициализировать с помощью var, вам придется изменить это, сделав их защищенными или общедоступными.

  2. Поскольку var использует правую часть для вывода типа, такие закрытые поля всегда нужно было инициализировать сразу. Если переместить инициализацию в конструктор, вам придется сделать тип явным.

  3. С var компилятор может выводить типы, которые вы в настоящее время не можете выразить в Java (например, типы пересечений, такие как Comparable & Serializable). Конечно, вы можете в конечном итоге полагаться на эти конкретные типы, и когда вам по какой-либо причине придется прекратить использование var в какой-то момент, вам может понадобиться довольно много рефакторинга, чтобы ваш код работал.

5
Nicolai 2 Мар 2018 в 14:27

Было бы разумным решением разрешить var для закрытых полей (IMO). Но пропуск этого делает функцию проще.

Кроме того, он может быть добавлен в какой-то будущий выпуск после того, как будет больше опыта с локальным выводом типа, тогда как удаление функции намного сложнее.

3
Alexey Romanov 2 Мар 2018 в 14:16

Разрабатывая ответ Николая (в частности, его причину № 2), предлагаемый черновик JLS 10 гласит, что оба var e; и var g = null; недопустимы для локальных переменных и по уважительной причине; с правой стороны (или без таковой) не ясно, какой тип выводить для var.

В настоящее время неконечные переменные экземпляра автоматически инициализируются в зависимости от их типа (примитивы к 0 и false и ссылки на null, как я уверен, вы уже знаете). Предполагаемый тип переменной экземпляра останется неясным, если он не будет инициализирован при объявлении или в конструкторе (ах) соответствующего класса.

По этой причине я поддерживаю возможность использования var только тогда, когда переменная является private и final, поэтому мы можем гарантировать, что она инициализируется к моменту создания класса. Хотя я не могу сказать, насколько сложно это было бы реализовать.

3
Jacob G. 2 Мар 2018 в 16:08

Не то, чтобы было совершенно невозможно превратить эти переменные в поля, которые можно проверить с помощью Reflection. Например, вы можете сделать

var l = new ArrayList<String>();
l.add("text");
System.out.println(l);
System.out.println(
  new Object(){ { var x = l; } }.getClass().getDeclaredFields()[0].getGenericType()
);

В текущей версии он просто печатает ArrayList, поэтому фактический универсальный тип не был сохранен в файле класса анонимного внутреннего класса, и вряд ли это изменится, поскольку поддержка этого самоанализа не является реальной целью , Это также просто особый случай, когда тип можно поменять как ArrayList<String>. Чтобы проиллюстрировать другой случай:

var acs = true? new StringBuilder(): CharBuffer.allocate(10);
acs.append("text");
acs.subSequence(1, 2);
System.out.println(
  new Object(){ { var x = acs; } }.getClass().getDeclaredFields()[0].getGenericType()
);

Тип acs является типом пересечения Appendable и CharSequence, что демонстрируется вызовом метода любого интерфейса на нем, но поскольку не указано, выводит ли компилятор {{X3 }} или #1 extends CharSequence&Appendable, не указано, будет ли код печатать java.lang.Appendable или java.lang.CharSequence.

Я не думаю, что это проблема для синтетического поля, но для явно объявленного поля это может быть.

Однако я сомневаюсь, что группа экспертов детально рассмотрела такое воздействие. Вместо этого решение не поддерживать декларации полей (и, следовательно, пропустить длительные размышления о последствиях) было принято с самого начала, поскольку локальные переменные всегда были целевой целью для этой функции. Количество локальных переменных намного больше, чем количество объявлений полей, поэтому наибольшее влияние имеет сокращение шаблона для объявлений локальных переменных.

5
Holger 2 Мар 2018 в 16:24