JavaRush /Курсы /Java Syntax Pro /Выведение типов Java-компилятором

Выведение типов Java-компилятором

Java Syntax Pro
12 уровень , 5 лекция
Открыта

1. Синтаксический сахар

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

Хитрые особенности языка, которые позволяют использовать более короткий путь (писать меньше кода), называют синтаксическим сахаром. Хотя, честно говоря, в Java его совсем немного.

Разработчики Java сделали все, чтобы устранить из Java всю возможную избыточность. Если в C++ что-то можно сделать десятью способами, в Java чаще всего это можно сделать только одним способом.

Но такая унификация не нравится ни Java-программистам, ни создателям Java. И иногда они упрощают жизнь обычным ребятам вроде нас с вами.

Вы, кстати, уже познакомились с вещью, которую можно отнести к синтаксическому сахару — это autoboxing и unboxing. Сравните:

Длинный код Компактный код
Integer a = new Integer(5);
int b = a.intValue();
Integer a = 5;
int b = a;
int b = 5;
Integer c = new Integer(b);
int b = 5;
Integer c = b;
Integer a = new Integer(1);
int b = 1;
if (a.intValue() == b)
{
   ...
}
Integer a = 1;
int b = 1;
if (a == b)
{
   ...
}

Вместо длинного кода как слева вы можете писать более компактный код, как справа. А умный Java-компилятор на основе краткого кода сам сгенерирует его полную версию. Это и есть синтаксический сахар.


2. Выведение типа переменной – var

В Java 11 компилятор стал еще умнее и теперь может определить тип создаваемой переменной по типу значения, которое ей присваивают. Выглядит это в коде так:

var имя = значение;

Где имя — это имя новой переменной, значение — ее стартовое значение, а var — это ключевое слово, используемое для объявления переменной. Тип у переменной имя будет такой же, как у значения, которое ей присваивают.

Примеры:

Как этот код видим мы Что видит компилятор
var i = 1;
int i = 1;
var s = "Привет";
String s = "Привет";
var console = new Scanner(System.in);
Scanner console = new Scanner(System.in);
var list = new ArrayList<String>();
ArrayList<String> list = new ArrayList<String>();
var data = new int[]{1, 2, 3};
int[] data = new int[]{1, 2, 3};

Компилятор сам определяет или, как еще говорят, выводит тип переменной на основе значения, которое ей присваивают.

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

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

Случай 1: глядя на значение переменной сразу ясно, какой тип у переменной

Код Пояснение
var stream = url.getInputStream();
У переменной тип InputStream
var name = person.getFullName();
У переменной тип String

А вот в этих случаях использовать var не стоит. Ну-ка ответьте, какой тип у переменной?

Код Пояснение
var result = task.execute();
Тип переменной определить сложно
var status = person.getStatus();
Тип переменной определить сложно

Случай 2: тип переменной не важен для понимания кода

Часто в коде могут быть ситуации, когда у переменной не вызываются никакие методы – переменная просто используется для временного хранения чего-либо. Использование var тут абсолютно не снижает понимание кода:

Длинный код Компактный код
var data = stream.getMetaData();
storage.save(data)
Мы получили метаданные из потока stream и сохранили их в хранилище storage. Какой именно тип был у переменной data — не важно.

Золотая середина

Сейчас приведу три способа записи одного и того же кода. Использование var будет оптимальным вариантом.

Код Примечание
dest.writeHeaderInfo(src.getFileMetaInfo());
Слишком компактно
var headerInfo = src.getFileMetaInfo();
dest.writeHeaderInfo(headerInfo);
Идеально
FileMetaInfo headerInfo = src.getFileMetaInfo();
dest.writeHeaderInfo(headerInfo);
Слишком подробно

Когда мы перешли от варианта в строке 1 к варианту в строке 2, мы за счет имени переменной (headerInfo) добавили коду немного читаемости. Теперь ясно, что метод возвращал не просто метаинформацию, а информацию о заголовке.

Третий вариант был бы избыточным. Ну и что, что headerInfo имеет тип FileMetaInfo — это и так было почти понятно по методу getFileMetaInfo(). Гораздо интереснее назначение этой метаинформации.



3. Опускание типа — оператор diamond: <>

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

ArrayList<String> list = new ArrayList<String>();

Начиная с седьмой версии Java, при записи типа коллекции можно было опускать (не писать) тип элементов коллекции, если он указан при объявлении переменной. Т.е. код выше можно записать немного в сокращенном виде:

ArrayList<String> list = new ArrayList<>();

Как вы видите, второй раз писать тип String больше не нужно. Не так круто, как с оператором var, но в свое время и это казалось достижением.

Пустые треугольные скобки в типе коллекции получили название оператор diamond: две скобки отдаленно напоминали силуэт бриллианта.

Использовать одновременно var и оператор diamond нежелательно:

var list = new ArrayList<>();

Информации о типе, который хранит коллекция, совсем не остается и тип будет определен как ArrayList<Object>.



4. Двойные фигурные скобки

Помните быструю инициализацию массива?

Мы там просто перечисляли значение в фигурных скобках:

Примеры
int[] data = new int[] {1, 2, 3, 4, 5, 6, 7};
int[] data = {1, 2, 3, 4, 5, 6, 7};

Создателям Java понравилась идея использовать фигурные скобки для упрощенной записи данных в массив. Но как быть с коллекциями?

Для коллекций тоже хватило фантазии: разрешили использовать трюк с двойными фигурными скобками.

С сахаром Без сахара
var list = new ArrayList<String>()
{{
   add("Привет");
   add("Как");
   add("Дела");
}};
var list = new ArrayList<String>();

list.add("Привет");
list.add("Как");
list.add("Дела");

Если компилятор встретит код в примере слева, он преобразует его в код справа.

Код не становится сильно компактнее. Тут скорее экономия на мелочах: не нужно каждый раз писать list. Это может быть выгодно, если имя переменной очень длинное.

Но если вы встретите в чьем-то проекте такой код, не удивляйтесь 🙂


Комментарии (246)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Grigoryvvv Уровень 14 Expert
16 января 2026
16.01.2026 / 13 уровень
C0N5P1RACY Уровень 21
18 декабря 2025
Пока не понятны были примеры с var, но в целом все хорошо расписано. Про var в новом курсе написано вообще чуть ли не в самом начале курса, а тут только на 13 уровне, это конечно круто, и стоит эта тема в идеальном месте, сразу после коллекций объяснили что можно упростить. В очередной раз убеждаюсь что классический курс лучший :)
Александр Е Уровень 1
29 ноября 2025

new Integer(5)
если что, такой конструктор (и для других классов-обёрток примитивных типов) давно деприкейтд
Alexander Pavlyuchenko Уровень 24
20 июля 2025
Вроде и максимально легки все эти задачки но очень помогают быть внимательным, кто согласен👍
Anonymous #3585174 Уровень 33
30 июня 2025
like
Vasiliy Уровень 20
16 июня 2025
Пункт 3 - "Опускание типа"...какая то ауешная терминология...🤣
Bilal Уровень 32
14 июня 2025

Можно использовать var:

Локальная переменная в методе   |   var name = “Text”;
Тело метода main()              |	var number = 42;
Цикл for-each                   |   for (var item : list) {…}
Обычный for                     |   for (var I = 0; I < 10; i++) {…}
В try-with-resources            |   try (var reader = new BufferedReader(…)) {…}

Нельзя использовать var:

Объявление полей класса (компилятор не может вывести тип)                   |   private var name = “Text”;
Параметры метода (типы параметров должны быть явными)                       |   public void greet(var name)
Сигнатура метода (метод должен явно указывать тип возвращаемого значения)   |   public var getName()
Объявление без инициализации (var требует значения сразу)                   |   var x;
Массив без инициализации (нужна четкая структура)                           |   var array = {1, 2, 3};
Юрий Болотин Уровень 3
26 апреля 2025
Приветствую, коллеги! Отмечу, что

var
предназначен исключительно для локальных переменных, и их тип определяется статически на этапе компиляции. Использование var для полей класса, параметров методов и возвращаемых типов не допускается.
Aura Уровень 23
18 мая 2025
Да, спасибо, а то как раз на задаче с заменой var, заменил поля класса и меня послали куда подальше =) Но и спасибо за дополнение(про параметры и возвращаемые типы)
Java-Самурай Уровень 13
3 июля 2025
Видел применение var на практике, только в цикле for each. Имхо то, что тут дают - излишне. Не вижу проблему явно указывать тип данных без всяких там var`ов.
Danya Уровень 17
13 апреля 2025
var strings = new ArrayList<String>(){{ add("Так"); add("тоже"); add("можно"); add("делать"); add("!"); }}; написал сам так код пишет неправильно. выбрал правильный ответ скопировал тот же самый код с ответа var strings = new ArrayList<String>(){{ add("Так"); add("тоже"); add("можно"); add("делать"); add("!"); }}; правильно🖕👍
19 апреля 2025
мб где то опечатался. часто вместо 'a' латиницей пишут 'a' кириллицей.
MotaxPLA_52 Уровень 15
18 мая 2025
Скорее всего, в первой попытке решения задачи, которая, как ты говоришь, "самостоятельно", ты забыл убрать точку с запятой после создания коллекции и перед добавлением значений в коллекцию. Чтобы было наглядно, напишу код, который вызвал у тебя ошибку:

var strings = new ArrayList<String>(); {{
        add("Так");
        add("тоже");
        add("можно");
        add("делать");
        add("!");
        }};
первая строка, сам посмотри, может в этом ошибка была
Danya Уровень 17
19 мая 2025
Думаешь?А тот факт что правильное решение не содержит в себе тоже точку с запятой?
9 апреля 2025
А вот валидатор ругается на двойные фигурные скобки и вот комментарий почему: Поскольку инициализация двойными скобками (DBI) создает анонимный класс со ссылкой на экземпляр объекта-владельца, ее использование может привести к утечке памяти, если анонимный внутренний класс возвращается и хранится другими объектами. Даже если утечки нет, DBI настолько неясен, что может сбить с толку большинство разработчиков. Для коллекций используйте вместо этого Arrays.asList или явно добавляйте каждый элемент непосредственно в коллекцию. Пример несоответствующего кода

Map source = new HashMap(){{ // Noncompliant
    put("firstName", "John");
    put("lastName", "Smith");
}};