Автор оригинала: Pankaj Kumar.
В этой статье мы бы глубоко погрузились в новую функцию вывода типа локальной переменной, представленную в Java 10. Мы рассмотрим область применения и ограничения использования вывода типа локальной переменной.
Эта функция была предложена в рамках JEP (Предложение по улучшению JDK): 286 . Предложение состояло в том, чтобы улучшить язык для поддержки вывода типа для объявления и инициализации локальной переменной.
1. Java 10: Вывод типа Локальной Переменной
В Java 10 вы можете использовать var для локальных переменных вместо введенного имени (Тип манифеста). Это делается с помощью новой функции, которая называется выводом типа локальной переменной.
Но сначала, Что такое вывод типа?
Вывод типа-это способность компилятора Java просматривать каждый вызов метода и соответствующее объявление, чтобы определить аргумент типа (или аргументы), которые делают вызов применимым. Вывод типов не относится к программированию на Java.
Для объявлений локальных переменных с инициализатором теперь мы можем использовать зарезервированное имя типа “var” вместо типа манифеста. Давайте рассмотрим несколько примеров.
var list = new ArrayList(); // infers ArrayList var stream = list.stream(); // infers Stream
Тип манифеста : Явная идентификация типа для каждой объявленной переменной называется типизацией манифеста. Например, если переменная “действующие лица” будет хранить список действующих лиц, то ее тип List<Актер> является типом манифеста, и его необходимо объявить (как указано ниже) до Java 10:
Listactors = List.of(new Actor()); // Pre Java 10 var actors = List.of(new Actor()); // Java 10 onwards
2. Как работает вывод типа локальной переменной?
Анализируя оператор var, компилятор просматривает правую часть объявления, он же инициализатор, и выводит тип из выражения правой части (RHS).
Хорошо, достаточно хорошо, означает ли это, что теперь Java является динамически типизированным языком? Не совсем, это все еще статически типизированный язык. Давайте возьмем фрагмент кода для чтения файла.
private static void readFile() throws IOException {
var fileName = "Sample.txt";
var line = "";
var fileReader = new FileReader(fileName);
var bufferedReader = new BufferedReader(fileReader);
while ((line = bufferedReader.readLine()) != null) {
System.out.println(line);
}
bufferedReader.close();
}
Теперь давайте посмотрим на декомпилированный код, взятый из декомпилятора IntelliJ IDEA.
private static void readFile() throws IOException {
String fileName = "Sample.txt";
String line = "";
FileReader fileReader = new FileReader(fileName);
BufferedReader bufferedReader = new BufferedReader(fileReader);
while ((line = bufferedReader.readLine()) != null) {
System.out.println(line);
}
bufferedReader.close();
}
Здесь компилятор правильно выводит тип переменной из выражения правой части и добавляет его в байт-код.
3. var-это зарезервированное имя типа
var-это не ключевое слово, это зарезервированное имя типа. Что это значит?
- Мы можем создать переменную с именем “var”.
- “var” в качестве имени метода допускается.
- “var” в качестве имени пакета допускается.
- “var” нельзя использовать в качестве имени класса или интерфейса.
4. Сценарии Использования Вывода Типа Локальной Переменной
Вывод локального типа может использоваться только в следующих сценариях:
- Ограничено только локальной переменной с инициализатором
- Индексы улучшены для цикла или индексов
- Локальный объявлен в цикле for
Давайте рассмотрим примеры для этих сценариев:
var numbers = List.of(1, 2, 3, 4, 5); // inferred value ArrayList// Index of Enhanced For Loop for (var number : numbers) { System.out.println(number); } // Local variable declared in a loop for (var i = 0; i < numbers.size(); i++) { System.out.println(numbers.get(i)); }
5. Ограничения на Вывод типа Локальной Переменной
Существуют определенные ограничения использования var, давайте рассмотрим некоторые из них.
- Невозможно использовать ‘var’ для переменных без инициализатора
Если инициализатора нет, то компилятор не сможет определить тип.
- Не может использоваться для определения нескольких переменных
- Значение Null нельзя использовать в качестве инициализатора для var
Null не является типом, и, следовательно, компилятор не может определить тип выражения RHS.
- Не может иметь дополнительных скобок размера массива
- Поли-выражения, содержащие лямбды, ссылки на методы и инициализаторы массивов, вызовут ошибку
Для вывода типа Лямбда-выражений , вывода метода и инициализаторов Массива компилятор полагается на левое выражение или определение аргумента метода, в котором передается выражение, в то время как var использует RHS, это приведет к циклическому выводу, и, следовательно, компилятор генерирует ошибку времени компиляции.
6. Универсальные методы с выводом типа локальной переменной
Java имеет вывод типов для универсальных и, кроме того, он также должен выполнять Стирание типов для любого оператора generics. Есть некоторые крайние случаи, которые следует понимать при использовании ссылки на локальный тип с универсальными.
Стирание типов : Для реализации универсальных типов компилятор Java применяет стирание типов ко всем параметрам типов в универсальных типах, заменяя их границами или объектом, если параметры типа не ограничены.
Давайте рассмотрим некоторые варианты использования var с использованием дженериков:
var map1 = new HashMap(); // Inferred as HashMap var map2 = new HashMap<>(); // Inferred as HashMap
map1 – Компилятор выводит карту как хэш-карту без какого-либо универсального типа.
map2 – Оператор diamond полагается на LHS для вывода типа, где компилятор не может вывести LHS, и, следовательно, он выводит, что map2 имеет верхнюю границу или супертип, к которому можно отнести хэш-карту. Это приводит к тому, что map2 выводится как HashMap
7. Анонимные Типы Классов
Анонимные типы классов не могут быть названы, но их легко понять – это просто классы. Разрешение переменным иметь анонимные типы классов вводит полезную сокращенную формулировку для объявления одноэлементного экземпляра локального класса. Давайте рассмотрим пример:
var runnable = new Runnable() {
@Override
public void run() {
var numbers = List.of(5, 4, 3, 2, 1);
for (var number : numbers) {
System.out.println(number);
}
}
};
runThread(runnable);
8. Не Обозначаемые Типы
Выражение, которое не может быть отнесено к определенному типу, известно как Не обозначаемый тип. Такой тип может встречаться для типа переменной захвата, типа пересечения или анонимного типа класса. Давайте разберемся, как можно использовать не обозначаемый тип для вывода типа локальной переменной:
var map3 = new HashMap<>() { // anonymous class
int someVar;
};
Здесь, когда оператор diamond используется с анонимным типом класса, компилятор не может вывести выражение RHS для какого-либо определенного типа. Это приводит к образованию неденотируемого типа.
Во-первых, компилятор получит обозначаемый тип, используя супертип для HashMap<>, который является HashMap<Объект, объект>.
Во-вторых, применяется расширение анонимного класса. Наконец, это становится Не обозначаемым типом, который присваивается карте 3.
Теперь можно создать особый случай Не обозначаемого типа, который ранее не удавалось создать в Java. Анонимное расширение класса объектов и добавление в него атрибутов создает POJO, подобный классу, который может быть назначен переменной для хранения контекста. Это может быть очень полезно при использовании динамически создаваемого объекта, который может иметь структуру во временном контексте. Давайте рассмотрим пример:
// Special Case Non-Denotable Type
var person = new Object() {
class Name {
String firstName;
String lastName;
public Name(String firstName, String lastName) {
super();
this.firstName = firstName;
this.lastName = lastName;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
}
Name name;
Actor actor;
public String displayName() {
return name.getFirstName() + " " + name.lastName;
}
};
person.name = person.new Name("Rakesh", "Kumar");
System.out.println(person.displayName());
9. Некоторые интересные факты о выборе var для вывода типа локальной переменной
Был опрос для списка ключевых слов на выбор, для вывода локального типа. Ниже приведен список синтаксических опций, предоставляемых пользователям сообщества:
- только var (например, C#)
- var, плюс val для неизменяемых местных жителей (например, Scala, Котлин)
- var, плюс пусть для неизменяемых местных жителей (например, Swift)
- авто (например, C++)
- const (уже зарезервированное слово)
- финал (уже зарезервированное слово)
- позволять
- деф (как заводной)
- x (нравится Идти)
Результаты опроса: Ответы на выбор в процентах:
Обоснование использования 2-го лучшего выбора (var)
- Несмотря на то, что var был 2-м лучшим выбором, людей это устраивало, и почти никто не ненавидел его прямо. В то время как это не относилось к другим вариантам.
- Опыт работы с C#. Сообщество C# сочло ключевое слово подходящим для языка, подобного Java.
- Некоторые читатели обнаружили, что var/val были настолько похожи, что они могли в основном игнорировать разницу, и было бы раздражающе использовать разные ключевые слова для неизменяемых и изменяемых переменных.
- Большинство локальных переменных фактически являются окончательными, и наказание за неизменность с помощью другой церемонии-это не то, что было целью JEP.
10. Преимущества вывода типа локальной переменной
- Это улучшает работу разработчиков
- Это уменьшает церемонию ввода кода
- Это уменьшает шаблонный код
- Повышает четкость кода
11. Заключение
В этой статье мы рассмотрели вывод локального типа и объяснили, почему var был выбран в качестве синтаксического варианта. Как обычно, вы можете проверить полный код на github здесь .