W3docs

Ключевое слово var в Java (вывод типов локальных переменных)

Используйте var для вывода типов локальных переменных в Java: когда это улучшает читаемость, а когда нет.

Начиная с Java 10, вы можете объявить локальную переменную с помощью var и позволить компилятору вывести её тип из инициализатора. var greeting = "hello"; полностью идентично — включая скомпилированный байткод — записи String greeting = "hello";: тип по-прежнему String, вы просто не написали его дважды. Это вывод типов локальных переменных: синтаксическое удобство, которое убирает избыточные имена типов, не делая Java динамически типизированным языком. Используемый грамотно, он устраняет шум; используемый небрежно — скрывает именно ту информацию, которая нужна читателю.

На этой странице рассказывается, что var делает и чего не делает, где именно он допустим, в каких случаях он оправдан, в каких вредит читаемости, а также приведена запускаемая программа, доказывающая, что выведенные типы соответствуют ожиданиям.

var — это вывод типов, а не динамическая типизация

Самое важное: var — это не новый тип «что угодно». Компилятор читает правую часть, определяет статический тип и фиксирует его. С этого момента переменная так же строго типизирована, как если бы вы написали тип вручную — вы не можете присвоить ей значение несовместимого типа, и выведенный тип зафиксирован на этапе компиляции.

var name = "Ada";   // name has static type String, forever
name = "Lovelace";  // fine, still a String
name = 42;          // compile error: int cannot be assigned to String

var — это зарезервированное имя типа, а не ключевое слово — вы по-прежнему можете использовать var как имя переменной или метода (хотя не стоит). Вывод типа активируется только в позиции объявления локальной переменной.

Где var допустим — и где нет

var работает только для локальных переменных с инициализатором. Компилятору нужна правая часть, чтобы прочитать тип; без неё выводить не из чего.

Позицияvar допустим?Причина
Локальная переменная с инициализаторомДаИнициализатор предоставляет тип
Индекс/элемент в циклах forДаВыражение цикла предоставляет тип
Переменная в try-with-resourcesДаВыражение ресурса предоставляет тип
Локальная переменная без инициализатораНетНечего выводить
Поля / переменные экземпляраНетВывод только для локальных — по замыслу
Параметры методовНетЗначения предоставляют вызывающие, а не инициализаторы
Возвращаемые типы методовНетТа же причина, что и у параметров
Инициализация только nullНетУ null нет конкретного типа
Параметры лямбда-выражений (без типа)Особый случай(var x, var y) -> ... допустимо с Java 11
var x;                       // error: cannot infer type, no initializer
var nothing = null;          // error: null has no type to infer
public var field = 1;        // error: var not allowed on fields
void m(var p) { }            // error: var not allowed on parameters

Реальная выгода: устранение громоздких дженериков

var оправдывает своё существование, когда имя типа длинное, повторяющееся или насыщено дженериками. Классический случай — объявление, в котором тип записывается полностью по обе стороны от =:

// Before: the type name is written twice
Map<String, List<Customer>> byCity = new HashMap<String, List<Customer>>();

// After: the right side already says everything
var byCity = new HashMap<String, List<Customer>>();

Он также хорошо работает с итераторами, Map.Entry из HashMap и другими многословными типами, которые не добавляют ясности при явном написании:

for (var entry : byCity.entrySet()) {     // Map.Entry<String, List<Customer>>
  System.out.println(entry.getKey() + " -> " + entry.getValue().size());
}

Когда НЕ следует использовать var

var помогает, когда тип очевиден из правой части, и мешает, когда это не так. Если читатель вынужден мысленно выполнять код, чтобы понять тип, — напишите тип явно.

var result = service.process(input);   // unclear: what does process return?
Order result = service.process(input); // clear: an Order

var flag = true;                       // fine, obviously boolean
var count = list.size();               // fine, obviously int

Остерегайтесь ловушки числовых литералов: var выводит тип самого литерала, а не тот тип, который вы могли иметь в виду.

var n = 100;        // int, not long  — for a long you must write 100L or long n
var f = 3.14;       // double, not float
byte b = 1;         // explicit type narrows; var b = 1 would be int

Избегайте var, когда это приводит к потере намеренного типа интерфейса. var list = new ArrayList<String>(); типизирует list как ArrayList<String>, а не List<String> — локально это нормально, но если вы намерены программировать через интерфейс, скажите об этом явно.

Рабочий пример, который можно запустить

Эта программа использует var во всех допустимых позициях — простые значения, обобщённая карта, цикл for-each, индексный цикл for — и применяет getClass().getSimpleName(), чтобы доказать: выведенные типы в точности соответствуют тому, что подразумевали правые части.

java— editable, runs on the server

Что можно извлечь из запуска:

  • greeting.getClass().getSimpleName() выводит String, доказывая, что var greeting = "hello" создал настоящий Stringvar — это вывод типа на этапе компиляции, а во время выполнения объект в точности соответствует тому, что подразумевал литерал; ничего динамического.
  • count + 1 = 43 и price * 2 = 19.98 подтверждают правила вывода числовых типов: 42 сделало count типом int, 9.99 сделало price типом double. Тип литерала — а не ваше намерение — решает всё; это та ловушка, о которой нужно помнить, когда вам нужен long или float.
  • scores type = HashMap показывает, что var захватил конкретный тип правой части HashMap, а не интерфейс Map; угловые скобки <String, List<Integer>> в правой части дали компилятору всё необходимое, хотя левая часть содержала лишь var.
  • total chars = 10 получается из for (var name : names), где name был выведен как String, поэтому name.length() вычислился корректно (3 + 3 + 4) — var работает в цикле for-each, выводя тип элемента из итерируемого.
  • 0..4 sum = 10 получается из for (var i = 0; ...), где i был выведен как int из литерала 0; индексный цикл — одно из наиболее чистых мест для использования var, потому что тип очевиден.

Практика

Практика
В каком из этих объявлений 'var' допустим и выводит тип, указанный в комментарии?
В каком из этих объявлений 'var' допустим и выводит тип, указанный в комментарии?

Связанные темы

Was this page helpful?