Представьте, что локальная переменная объявлена через var: изменится ли её тип после присваивания объекта другого класса?
Нет. В Java var не означает динамическую типизацию: тип локальной переменной выводится один раз во время компиляции по её начальному выражению и затем не меняется. Последующие присваивания должны быть совместимы именно с выведенным типом.
var появился в Java 10, чтобы сократить избыточное повторение очевидных типов локальных переменных. При этом Java сохранила статическую типизацию: компилятор по-прежнему знает тип переменной до запуска программы.
Подход решает проблему длинных объявлений, особенно при работе с обобщёнными типами, но не превращает Java в язык с динамическими переменными.
Ошибка возникает, когда var ошибочно воспринимают как аналог динамического типа. Разработчик может ожидать, что после присваивания объекта другого класса переменная автоматически изменит свой тип.
На практике это приводит к ошибкам компиляции, неожиданному выбору доступных методов или чрезмерному использованию явных приведений типов. Кроме того, неверный выбор начального выражения может вывести слишком конкретный тип и ограничить дальнейшее использование переменной.
Компилятор анализирует инициализирующее выражение и фиксирует его тип как тип переменной. Это происходит только для локальных переменных и локальных переменных цикла; var нельзя использовать для полей, параметров методов или возвращаемого типа.
В первом случае тип переменной — StringBuilder, поэтому другое значение должно быть также совместимо с StringBuilder. Во втором случае тип — ArrayList<String>, а не List<String>: тот факт, что LinkedList<String> тоже реализует List<String>, не делает его совместимым с уже выведенным типом ArrayList<String>.
Тип выводится статически, поэтому набор доступных методов и проверка присваиваний определяются на этапе компиляции. Нельзя вывести тип без инициализатора, из литерала null, а также из лямбда-выражения или ссылки на метод без целевого функционального интерфейса.
Компромисс заключается в балансе между краткостью и явностью. var удобен, когда тип очевиден из правой части, но явное объявление лучше, если тип важен для понимания контракта или намерения кода.
В коде обработки коллекций разработчик заменил явные объявления на var. Для списка, создаваемого непосредственно через new ArrayList, после этого типом переменной стал ArrayList, хотя дальнейшая логика была рассчитана на абстракцию List.
Вариант с var уменьшает объём кода, но может связать переменную с конкретной реализацией коллекции. Вариант с явным List<String> лучше выражает требуемую абстракцию и позволяет заменить реализацию без изменения типа переменной, хотя требует написать больше текста.
Было выбрано явное объявление List<String>, поскольку переменная использовалась как часть алгоритма, которому важен интерфейс, а не конкретный класс. var оставили для локальных объектов, чей конкретный тип очевиден и действительно нужен.
var без начального значения?Нет. Компилятору необходимо выражение, по которому он сможет вывести тип. Поэтому отложенная инициализация с var невозможна: иначе статический тип переменной был бы неизвестен в момент компиляции.
var для параметра метода или поля класса?Нет, в обычных объявлениях Java var разрешён только для локальных переменных, переменных цикла и переменных в конструкциях try-with-resources. Тип параметра и поля является частью API или структуры класса, поэтому он должен быть явно указан.
Нет. Если начальное выражение создаёт конкретный класс, var обычно выводит именно этот конкретный тип, а не один из его интерфейсов. Поэтому var может раскрыть больше методов, чем переменная, объявленная через интерфейс, но одновременно сильнее связать код с конкретной реализацией.