В недавнем выпуске JDK 14 в общей сложности 16 основных улучшений. В этой статье я расскажу вам о самых интересных, в основном связанных с поддержкой языка.

1. Записи (функция предварительного просмотра)
Наверное, самая впечатляющая часть нового выпуска. Это новый вид объявления типа в Java.

public record User(long id, String name) {}

Всего с одной строкой мы получаем новый конечный класс, делая все его поля также конечными. Во время компиляции record автоматически сгенерирует шаблон constructors, public get, equals(), hashCode(), toString(). Сеттеров не будет, так как все поля окончательные.

Мы можем начать использовать нового пользователя record, как мы привыкли использовать классы:

public static void main(String[] args) {
    User user = new User(1L, "Mark");
    user.id();
    user.name();
}

Записи служат носителем данных и имеют строгие ограничения для поддержки неизменяемости из коробки.

Они не могут быть абстрактными, расширять любой другой класс, даже его неявный суперкласс. Это потому, что record’s API определяется состоянием, которое он поддерживает, и не может позволить своему составному классу изменять его.

По той же причине record не может иметь объявление собственных методов, потому что не может зависеть от внешней логики.

public native String getSystemTime(); //compilation error

static {
    System.loadLibrary("nativedatetimeutils");
}

record не может явно объявлять поля экземпляра или иметь методы установки. Только заголовок записи определяет состояние значения записи. Вот почему приведенный ниже код не компилируется:

void setId(long id){
    this.id = id; //compilation error
}

record можно сериализовать в JSON с помощью одной из ваших любимых библиотек Java, например Gson или Jackson:

Gson gson = new Gson();

String userJson = gson.toJson(user);
System.out.println(userJson); //outputs {"id":1,"name":"Mark"}

И десериализовали обратно:

User newUser = gson.fromJson(userJson, User.class);
System.out.println(newUser); //outputs User[id=1, name=Mark]

Еще один интересный факт, что суперкласс каждого record - это сама запись:

Class<?> superclass = user.getClass().getSuperclass();
System.out.println(superclass); //class java.lang.Record

К сожалению, records нельзя использовать в качестве легких объектов домена сохраняемости, каждый из которых представляет таблицу в реляционной базе данных. Это потому, что JPA требует конструктора без аргументов, которого нет в записях. Поставщик JPA должен получать и устанавливать поля объекта с помощью отражения, что также невозможно, поскольку поля записей являются окончательными. Таким образом, следующий код не скомпилируется:

@Entity
public record User(long id, String name) { //compilation error

}

Вам нужно будет продолжать использовать старый известный Lombok:

@Getter
@NoArgsConstructor
@AllArgsConstructor
public class Product {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Size(max = 40)
    private String name;
}

2. Соответствие шаблона instanceof (предварительная версия)

До Java 14, если вы хотели сделать instanceof проверку, а затем преобразовать объект в переменную, вы должны были сделать следующее:

if (obj instanceof User) {
    String s = (User) obj;
    // use s
}

В Java 14 это можно упростить до одной строки:

if (obj instanceof String s) {
    // can use s here
}

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

3.Текстовые блоки (второй просмотр)

Эта функция добавляет text blocks к языку Java. Текстовый блок - это строковый литерал, состоящий из нескольких строк. Использование text blocks помогает избежать большинства escape-последовательностей и конкатенации строк. В целом это упрощает процесс написания программ, позволяя легко выражать строки, которые занимают несколько строк исходного кода.

До Java 14:

String someHtml = "<html>\n" +
        "   <body>\n" +
        "      <p>Hello World</p>\n" +
        "   </body>\n" +
        "</html>\n";

С Java 14:

String java14 = """
                <html>
                    <body>
                        <p>Hello World</p>
                    </body>
                </html>
        """;

4. переключение выражений

Switch Expressions был функцией предварительной версии в Java 12 и Java 13, а с Java 14 она стала стандартной функцией языка. Новый переключатель можно использовать как выражение с использованием синтаксиса стрелок:

switch (article.state()) {
    case DRAFT -> System.out.println(1);
    case PUBLISHED -> System.out.println(2);
    case UNKNOWN -> System.out.println(3);
}

До Java 14:

switch (article.state()) {
    case DRAFT:
        System.out.println(1);
        break;
    case PUBLISHED:
        System.out.println(2);
        break;
    case UNKNOWN:
        System.out.println(3);
        break;
}

И теперь может выдавать / возвращать значение

int result = switch (article.state()) {
    case DRAFT -> 6;
    case PUBLISHED -> 7;
    case UNKNOWN -> 8;
};

До Java 14:

int result;
switch (article.state()) {
    case DRAFT:
        result = 6;
        break;
    case PUBLISHED:
        result = 7;
        break;
    case UNKNOWN:
        result = 8;
        break;
    default:
        throw new UnsupportedOperationException();
}

5.Полезные исключения NullPointerExceptions

Эта функция поможет быстрее отслеживать и разрешать исключения NullPointerExceptions, создаваемые JVM. До Java 14 эти сообщения были совсем неинформативными:

public static void main(String[] args) {
    User user = new User(1L, null);

    System.out.println(toUpperCase(user));
}

private static String toUpperCase(User user) {
    return user.name().toUpperCase(); // produces NPE
}

Сообщение будет выглядеть так:

«Исключение в потоке« main »java.lang.NullPointerException
в demo.Main.main (Main.java:16)»

Благодаря новому усовершенствованию, добавленному к параметрам виртуальной машины «XX: + ShowCodeDetailsInExceptionMessages», сообщение будет выглядеть так:

Исключение в потоке «main» java.lang.NullPointerException: невозможно вызвать «String.toUpperCase ()», поскольку возвращаемое значение «test.User.name ()» равно null
в demo.Main.toUpperCase (Main.java:20)
на demo.Main.main (Main.java:16)

Резюме

Обзор новейших возможностей Java 14 подошел к концу. Теперь у вас есть знания и практический опыт работы с новейшими функциями в мире Java. Надеюсь, вам понравилась эта статья и вы нашли ее полезной. Продолжайте тренироваться, ведь завтрашняя битва будет выиграна во время сегодняшней тренировки!