1. Введение
Java 15 стала общедоступной в сентябре 2020 года и является следующим краткосрочным выпуском для платформы JDK. Он основан на нескольких функциях из более ранних выпусков, а также предоставляет некоторые новые улучшения.
В этом посте мы рассмотрим некоторые новые функции Java 15 , а также другие изменения, представляющие интерес для Java-разработчиков.
2. Записи (JEP 384)
Запись — это новый тип класса в Java, который
упрощает создание неизменяемых объектов данных.
Первоначально представленная в Java 14 в качестве раннего предварительного просмотра, Java 15 направлена на улучшение некоторых аспектов , прежде чем стать официальной функцией продукта.
Давайте посмотрим на пример с использованием текущей версии Java и на то, как она может измениться с помощью записей .
2.1. Без записей
Перед записями мы создадим неизменяемый объект передачи данных (DTO) как:
public class Person {
private final String name;
private final int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
}
Обратите внимание, что здесь много кода для создания неизменяемого объекта, который просто хранит состояние. Все наши поля явно определены с помощью final
, у нас есть единственный конструктор для всех аргументов, и у нас есть метод доступа для каждого поля. В некоторых случаях мы могли бы даже объявить сам класс как final
, чтобы предотвратить любое подклассирование.
Во многих случаях мы также пойдем еще дальше и переопределим метод toString
, чтобы обеспечить значимые выходные данные журнала. Вероятно, мы также хотели бы переопределить методы equals
и hashCode
, чтобы избежать непредвиденных последствий при сравнении двух экземпляров этих объектов.
2.2. С записями
Используя новый класс записи
, мы можем определить тот же неизменяемый объект данных гораздо более компактным способом:
public record Person(String name, int age) {
}
Здесь произошло несколько вещей. Прежде всего, определение класса имеет новый синтаксис, специфичный для записи
s . В этом заголовке мы предоставляем подробную информацию о полях внутри записи.
Используя этот заголовок, компилятор может вывести внутренние поля. Это означает, что нам не нужно определять конкретные переменные-члены и методы доступа, поскольку они предоставляются по умолчанию. Нам также не нужно предоставлять конструктор.
Кроме того, компилятор предоставляет разумные реализации для методов toString
, equals
и hashCode .
Несмотря на то, что записи
избавляют от большого количества шаблонного кода, они позволяют нам переопределить некоторые поведения по умолчанию . Например, мы могли бы определить канонический конструктор, который выполняет некоторую проверку:
public record Person(String name, int age) {
public Person {
if(age < 0) {
throw new IllegalArgumentException("Age cannot be negative");
}
}
}
Стоит отметить, что у записей
есть некоторые ограничения. Среди прочего, они всегда final
, их нельзя объявить abstract
, и они не могут использовать нативные методы .
3. Запечатанные классы (JEP 360)
В настоящее время Java не предоставляет детального контроля над наследованием . Модификаторы доступа, такие как public
, protected
, private
, а также по умолчанию package-private обеспечивают очень грубый контроль.
С этой целью цель запечатанных
классов состоит в том, чтобы позволить отдельным классам объявлять, какие типы могут использоваться в качестве подтипов. Это также относится к интерфейсам и определению того, какие типы могут их реализовать.
Закрытые классы включают два новых ключевых слова — запечатанные
и разрешения
:
public abstract sealed class Person
permits Employee, Manager {
//...
}
В этом примере мы объявили абстрактный
класс с именем Person.
Мы также указали, что единственными классами, которые могут его расширить, являются Employee
и Manager
. Расширение запечатанного
класса выполняется так же, как и сегодня в Java, с использованием ключевого слова extends
:
public final class Employee extends Person {
}
public non-sealed class Manager extends Person {
}
Важно отметить, что любой класс, который расширяет запечатанный
класс, сам должен быть объявлен запечатанным
, незапечатанным
или окончательным
. Это гарантирует, что иерархия классов останется конечной и известной компилятору.
Эта конечная и исчерпывающая иерархия является одним из больших преимуществ использования запечатанных
классов . Давайте посмотрим на пример этого в действии:
if (person instanceof Employee) {
return ((Employee) person).getEmployeeId();
}
else if (person instanceof Manager) {
return ((Manager) person).getSupervisorId();
}
Без запечатанного класса компилятор не может разумно определить, что все возможные подклассы покрыты нашими операторами if-else
. Без предложения else
в конце компилятор, скорее всего, выдаст предупреждение о том, что наша логика не охватывает все случаи.
4. Скрытые классы (JEP 371)
Новая функция, представленная в Java 15, известна как скрытые классы . Хотя большинство разработчиков не найдут в них прямой выгоды, любой, кто работает с динамическим байт-кодом или языками JVM, скорее всего, найдет их полезными.
Цель скрытых классов — позволить во время выполнения создавать классы, которые невозможно обнаружить . Это означает, что они не могут быть связаны другими классами и не могут быть обнаружены с помощью отражения . Такие классы обычно имеют короткий жизненный цикл, поэтому скрытые классы спроектированы таким образом, чтобы эффективно выполнять как загрузку, так и выгрузку.
Обратите внимание, что текущие версии Java позволяют создавать анонимные классы, подобные скрытым классам. Однако они полагаются на небезопасный
API. Скрытые классы не имеют такой зависимости.
5. Проверки типа соответствия шаблону (JEP 375)
Функция сопоставления с образцом была предварительно представлена в Java 14, а Java 15 стремится сохранить статус предварительной версии без каких-либо новых улучшений.
В качестве обзора, цель этой функции — удалить много шаблонного кода, который обычно поставляется с оператором instanceof :
if (person instanceof Employee) {
Employee employee = (Employee) person;
Date hireDate = employee.getHireDate();
//...
}
Это очень распространенный шаблон в Java. Всякий раз, когда мы проверяем, относится ли переменная к определенному типу, мы почти всегда выполняем приведение к этому типу.
Функция сопоставления с образцом упрощает это, вводя новую переменную привязки
:
if (person instanceof Employee employee) {
Date hireDate = employee.getHireDate();
//...
}
Обратите внимание, как мы предоставляем новое имя переменной, employee
, как часть проверки типа. Если проверка типа верна
, то JVM автоматически приводит переменную для нас и присваивает результат новой переменной привязки .
Мы также можем комбинировать новую переменную привязки с условными операторами:
if (person instanceof Employee employee && employee.getYearsOfService() > 5) {
//...
}
В будущих версиях Java цель состоит в том, чтобы расширить сопоставление с образцом для других функций языка, таких как операторы switch
.
6. API внешней памяти (JEP 383)
Доступ к внешней памяти уже является инкубационной функцией Java 14. В Java 15 цель состоит в том, чтобы продолжить инкубационный статус, добавив несколько новых функций:
- Новый API
VarHandle
для настройки дескрипторов переменных доступа к памяти . - Поддержка параллельной обработки сегмента памяти с помощью интерфейса
Spliterator
- Расширенная поддержка
отображаемых
сегментов памяти. - Возможность манипулировать и разыменовывать адреса, исходящие от таких вещей, как собственные вызовы.
Внешняя память обычно относится к памяти, которая находится за пределами управляемой кучи JVM. Из-за этого он не подлежит сборке мусора и обычно может обрабатывать невероятно большие сегменты памяти.
Хотя эти новые API, скорее всего, не повлияют напрямую на большинство разработчиков, они принесут большую пользу сторонним библиотекам, работающим с внешней памятью. Это включает в себя распределенные кэши, денормализованные хранилища документов, большие произвольные буферы байтов, отображаемые в память файлы и многое другое.
7. Сборщики мусора
В Java 15 и ZGC (JEP 377), и Shenandoah (JEP 379) перестанут быть экспериментальными . Обе будут поддерживаться конфигурациями, которые команды могут выбрать для использования, в то время как сборщик G1 останется по умолчанию.
Оба ранее были доступны с использованием флагов экспериментальных функций. Такой подход позволяет разработчикам тестировать новые сборщики мусора и отправлять отзывы, не загружая отдельный JDK или надстройку.
Одно замечание по поводу Shenandoah : он доступен не во всех JDK поставщиков — особенно в Oracle JDK его нет.
8. Другие изменения
В Java 15 есть несколько других заслуживающих внимания изменений.
После нескольких раундов предварительного просмотра в Java 13 и 14 текстовые блоки станут полностью поддерживаемой функцией продукта в Java 15.
Полезные исключения нулевого указателя , изначально поставляемые в Java 14 под JEP 358, теперь включены по умолчанию.
Устаревший API DatagramSocket
был переписан. Это продолжение переписанного в Java 14 Socket
API. Хотя это не повлияет на большинство разработчиков, это интересно, поскольку является необходимым условием для Project Loom .
Также следует отметить, что Java 15 включает криптографическую поддержку алгоритма цифровой подписи Эдвардса-Кривой. EdDSA — это современная схема подписи на эллиптических кривых, которая имеет ряд преимуществ по сравнению с существующими схемами подписи в JDK.
Наконец, некоторые вещи устарели в Java 15. Предвзятая блокировка, порты Solaris/SPARC и активация RMI удалены или запланированы к удалению в будущем выпуске.
Следует отметить, что движок Nashorn JavaScript, изначально представленный в Java 8, теперь удален. С недавним внедрением GraalVM и других технологий VM стало ясно, что Nashorn больше не имеет места в экосистеме JDK.
9. Заключение
Java 15 основывается на нескольких функциях прошлых выпусков, включая записи, текстовые блоки, новые алгоритмы сборки мусора и многое другое. Он также добавляет новые функции предварительного просмотра, в том числе запечатанные классы и скрытые классы .
Поскольку Java 15 не является выпуском с долгосрочной поддержкой, мы можем ожидать, что его поддержка прекратится в марте 2021 года. В то же время мы можем ожидать Java 16, за которой вскоре последует новая версия с долгосрочной поддержкой в Ява 17.