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. Это может быть выгодно, если имя переменной очень длинное.

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