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(). Гораздо интереснее назначение этой метаинформации.


undefined
13
Задача
Java Syntax Pro, 13 уровень, 6 лекция
Недоступна
Вперед в будущее
В классе Solution есть поля number, string и метод main с объявленными в нем переменными. Тебе нужно заменить объявления переменных на var, где это возможно. Порядок следования переменных не должен меняться. Пример: Scanner console = new Scanner(System.in); Заменить на: var console = new Scanner(
undefined
13
Задача
Java Syntax Pro, 13 уровень, 6 лекция
Недоступна
Назад в прошлое
В классе Solution есть поле intArray, геттер и сеттер для него, и метод main с объявленными в нем переменными. Тебе нужно заменить типы переменных с var на конкретный тип объекта (изменять названия и инициализацию объектов нельзя), а также починить геттер и сеттер поля intArray. Пример: var console

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>.


undefined
13
Задача
Java Syntax Pro, 13 уровень, 6 лекция
Недоступна
Shine bright like a diamond
В методе main создаются объекты разных типов. В некоторых случаях тип элементов объекта можно опустить и просто использовать оператор diamond(<>). Замени тип элементов объекта на оператор diamond там, где это возможно, не меняя левую часть выражений. Порядок следования переменных не должен меняться.

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

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


undefined
13
Задача
Java Syntax Pro, 13 уровень, 6 лекция
Недоступна
Двойные фигурные скобки
В методе main создается список ArrayList, присваивается переменной var strings и заполняется пятью элементами. Нужно переписать код, не изменяя логику, используя двойные фигурные скобки.