Рубрики
Без рубрики

Весенняя загрузка и Java 16 Записи

В этой статье мы обсудим новейшую функцию Java 16 – Записи. Затем мы применим эти знания… С пометкой java, весенняя загрузка, программирование, учебник.

В этой статье мы обсудим новейшую функцию Java 16 – Записи. Затем мы применим эти знания и будем использовать их в сочетании с приложением Spring Boot.

Вступление

16 марта 2021 года была запущена Java 16. В этом новом выпуске добавлено множество новых интересных функций. Ознакомьтесь с примечаниями к выпуску , чтобы узнать больше об этих изменениях в деталях. В этой статье основное внимание будет уделено записям Java, которые были доставлены с JEP 395 . Записи были впервые представлены в JDK 14 в качестве функции предварительного просмотра, предложенной JEP 359, а с JDK15 они остались в режиме предварительного просмотра с JEP 384. Однако в JDK 16 записи больше не отображаются в режиме предварительного просмотра.

Я выбрал записи, потому что они определенно являются наиболее популярной функцией, добавленной в Java 16, согласно этому опросу в Твиттере, проведенному чемпионом Java Малой Гуптой.

Я также провел аналогичный опрос, но он был сосредоточен на функциях начиная с Java 8 и далее. Лямбды, Поток и Опционально получили наибольшее количество голосов в этом опросе. Результаты не были неожиданными, так как Java 8 все еще широко используется. Однако это очень прискорбно, так как в новые версии Java добавляется множество новых функций и улучшений. Но с точки зрения функций Java 8 определенно изменила правила игры с точки зрения разработчика.

Для получения дополнительной информации о Java и технологиях, следуйте за мной на Твиттер

Итак, давайте обсудим, что за шумиха вокруг записей Java.

Что такое Записи?

В соответствии с JEP 395:

Записи – это новый вид класса в языке Java. Они действуют как прозрачные носители неизменяемых данных с меньшими церемониями, чем обычные классы. Записи можно рассматривать как номинальные кортежи.

Другая цитата из JEP ясно объясняет разочарование разработчиков при написании типичных классов носителей данных.

Правильное написание такого класса носителей данных включает в себя множество малоценного, повторяющегося, подверженного ошибкам кода: конструкторы, средства доступа, равенства, хэш-код, toString и т. Д. Например, класс для переноса координат x и y неизбежно заканчивается так:

class Point {
    private final int x;
    private final int y;

    Point(int x, int y) {
        this.x = x;
        this.y = y;
    }

    int x() { return x; }
    int y() { return y; }

    public boolean equals(Object o) {
        if (!(o instanceof Point)) return false;
        Point other = (Point) o;
        return other.x == x && other.y == y;
    }

    public int hashCode() {
        return Objects.hash(x, y);
    }

    public String toString() {
        return String.format("Point[x=%d, y=%d]", x, y);
    }
}

Еще один вариант, который мы, разработчики, используем чаще всего, – оставить обработку шаблона IDE. Например, с помощью Intellij вы можете создавать конструкторы, геттеры, сеттеры, равенства, хэш-код, строку и т. Д., Просто нажав сочетание клавиш Command + N. Но шаблонный код все еще существует.

С записями Java 16 это всего лишь одна строка кода. Круто, не правда ли?

record Point(int x, int y) { }

Здесь объявление класса записи состоит из имени, необязательных параметров типа, заголовка и тела.

Разоблачающие записи

Внутренние компоненты класса Java record можно проверить с помощью декомпилятора, входящего в состав IntelliJ IDE, или с помощью утилиты командной строки javap . Чтобы понять внутренние компоненты, мы создали следующий класс записей.

public record State(String name, String capital) {}

Ниже приведен декомпилированный класс записи Java. Я использовал утилиту командной строки java для проверки внутренних файлов классов.

 $ javap State.class

Ниже приведен вывод.

Compiled from "State.java"
public final class com.example.indianstates.State extends java.lang.Record {
  public com.example.indianstates.State(java.lang.String, java.lang.String);
  public final java.lang.String toString();
  public final int hashCode();
  public final boolean equals(java.lang.Object);
  public java.lang.String name();
  public java.lang.String capital();
}

Из приведенного выше вывода вы можете сделать следующий вывод.

  1. Класс записи состояния расширяет файл java.lang. Запись абстрактный класс.
  2. Класс записи состояния объявлен окончательным и не может быть расширен с помощью ключевого слова extends.
  3. Хэш-код(), равно(), toString() и канонический конструктор неявно генерируются для нас.
  4. Здесь нет сеттеров или геттеров, только средства доступа.
  5. Без установщиков и финала в объявлении класса разъясняется, что вы не можете изменить состояние, и, следовательно, записи являются неизменяемыми.

Вы также можете дополнительно проверить эти пункты, написав тесты.

    @Test
    public void testRecordAccessors() {
        String name = "Maharashtra" ;
        String capital = "Mumbai" ;
        State state = new State("Maharashtra", "Mumbai");
        Assert.assertEquals(name, state.name());
        Assert.assertEquals(capital, state.capital());
    }

    @Test
    public void testRecordToString() {
        State state = new State("Maharashtra", "Mumbai");
        System.out.println(state);
        //Output:-State[name=Maharashtra, capital=Mumbai]
    }

    @Test
    public void testRecordEquals() {
        State state1 = new State("Maharashtra", "Mumbai");
        State state2 = new State("Maharashtra", "Mumbai");
        Assert.assertTrue(state1.equals(state2));
    }

    @Test
    public void testRecordHashCode() {
        State state1 = new State("Maharashtra", "Mumbai");
        State state2 = new State("Maharashtra", "Mumbai");
        Assert.assertEquals(state1.hashCode(), state2.hashCode());
    }

Существуют некоторые ограничения в объявлении классов записей по сравнению с обычными классами. Оформите заказ JEP 395 для таких ограничений.

Ломбок и Рекорды, Друзья или враги?.

Вы, вероятно, уже используете Ломбок аннотации, такие как @ Значение , которое ближе всего, если не совпадает, к записям Java. Тогда вы сможете избавиться от одной зависимости и этих рождественских елок аннотаций. Возможно, я слишком упростил ситуацию, и в некоторых случаях имеет смысл заменить Ломбок. Но вы можете использовать Lombok для других функций, а не только для одной аннотации, которую он предоставляет. И поверьте мне, хотя записи Java являются желанной функцией для любителей Java, но она не заменит Ломбок, по крайней мере, на данный момент. Вы мне не верите? Ознакомьтесь с этим ответом от Брайана Гетца на StackOverflow.

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

Весенняя загрузка и записи Java

Начиная с версии 2.5.0-M1 и далее, Spring Boot обеспечивает предварительную поддержку для Java 16. У меня есть работающее приложение Spring Boot, которое я буду использовать для демонстрации Java записывает с его помощью. Исходный код доступен здесь . Это простое приложение для загрузки Spring, которое при доступе через конечную точку/states или REST показывает все или конкретные штаты Индии и их столицы. Это приложение использует базу данных H2 в памяти, которая вставляет строки в начале приложения.

Как всегда, вы можете использовать start.spring.ввод-вывод для создания заглушек для вашего приложения Spring Boot, и, как объяснялось ранее, убедитесь, что вы выбрали версию 2.5.x milestone.

Вот как выглядит класс контроллера REST.

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
public class Controller {
    private final StateService stateService;

    public Controller(StateService stateService) {
        this.stateService = stateService;
    }

    @GetMapping("/states")
    private List getAllStates() {
        return stateService.findAll();
    }

    @GetMapping(value = "/state")
    private String getSpecificState(@RequestParam(required = false, name = "name", defaultValue = "Maharashtra") String name) {
        return stateService.findByName(name);
    }
}

Мы можем сосредоточиться на методе getAllStates(), который возвращает список объектов класса записей состояния.

Мы уже видели класс государственных рекордов. В этом нет никаких изменений.

public record State(String name, String capital) {}

Ниже приведен интерфейс Государственного хранилища , реализованный классом Государственной службы .

import java.util.List;

public interface StateRepository {
    List findAll();

    String findByName(String name);
}
@Service
public class StateService implements StateRepository{
    private final JdbcTemplate jdbcTemplate;

    public StateService(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }
    private final RowMapper  rowMapper = (rs, rowNum) -> new State(rs.getString("name"),rs.getString("capital"));

    @Override
    public List findAll() {
        String findAllStates = """
                select * from States
                """;
        return jdbcTemplate.query(findAllStates, rowMapper);
    }

    @Override
    public String findByName(String name) {
        String findByName = """
                select capital from States where name = ?;
                """;
        return jdbcTemplate.queryForObject(findByName, String.class, name);
    }
}

Государственная служба автоматически подключается с помощью конструктора класса Контроллер . В нем есть метод с именем Findall(), который использует Spring JdbcTemplate для запроса и возвращает список классов состояния записи из базы данных H2 в памяти. Как вы можете видеть, мы использовали функциональный интерфейс RowMapper , который JdbcTemplate использует для сопоставления строк результирующего набора для каждой строки, и он возвращает объект строки для текущей строки. Мы также использовали ключевое слово new для инициализации класса записи, что означает, что мы можем инициализировать класс записи, как обычные классы в Java. Я также использовал Java 15 Текстовые блоки функция, которая помогает в удобочитаемости SQL-запросов и строковых значений JSON.

Однако возникли некоторые проблемы, когда я начал использовать записи с этим приложением. Раньше я использовал BeanPropertyRowMapper, что привело к следующему исключению, когда я отключил Ломбок и вместо этого использовал записи для класса Состояние .

2021-03-19 02:01:55.434 ERROR 66059 --- [nio-8080-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.example.indianstates.State]: No default constructor found; nested exception is java.lang.NoSuchMethodException: com.example.indianstates.State.()] with root cause

Из исключения и BeanPropertyRowMapper |/документация совершенно очевидно, что мы должны объявить конструктор по умолчанию или без аргументов в классе State records, что приводит к некоторым интересным открытиям о классах записей в Java.

Чтобы устранить эту ошибку, я наивно добавил конструктор без аргументов в класс Состояние запись.

public record State(String name, String capital) {
    public State() {
    }
}

Но это привело к следующей ошибке компиляции.

Неканонический конструктор записей должен делегировать другому конструктору

Чтобы решить эту проблему, я добавил следующий конструктор, но это приведет к тому, что значения будут равны нулю в ответе.

public record State(String name, String capital) {
    public State() {
        this(null,null);
    }
}

Затем я воспользовался помощью функции IntelliJ для создания конструктора для этого класса записей. Это предоставило мне следующие варианты.

Я попробовал эти варианты, но получил тот же результат. Я уже знал, что эти варианты не сработают, но я попытал счастья, что заставляет меня задуматься, как использовать записи с BeanPropertyRowMapper. У меня сейчас нет ответа на этот вопрос, но я буду копать дальше. Если вы увидите какие-либо проблемы с кодом или у вас есть лучший ответ, дайте мне знать.

Обновление: – Я разместил эти исключения в чате Spring Boot Gitter и получил этот ответ.

BeanPropertyRowMapper нельзя использовать с записями, поскольку он состоит из создания экземпляра JavaBean с его конструктором без аргументов, а затем вызова его установщиков для заполнения компонента. Но записи не имеют конструктора без аргументов и являются неизменяемыми и, следовательно, не имеют установщиков. Итак, либо используйте традиционный JavaBean, либо используйте записи, но тогда не используйте BeanPropertyRowMapper.

Справедливо. Так что очевидно, что BeanPropertyRowMapper нельзя использовать с записями.

На данный момент это конец. Счастливого кодирования.

Вывод

В этой статье вы узнали, что записи являются неизменяемыми классами носителей данных, которые сокращают количество шаблонного кода, который мы привыкли писать. Затем мы посмотрели на внутренние компоненты класса записи и обнаружили, что хэш-код(), равно(), toString() и конструкторы неявно генерируются для нас компилятором. Затем мы узнали, что на самом деле не следует сравнивать или заменять записи внешними библиотеками, такими как Lombok, потому что это разные инструменты для разных вещей.

В последнем разделе мы обнаружили, что записи хороши для использования в таких случаях, как получение данных из базы данных (или какой-либо внешней службы) на примере приложения Spring Boot. Мы также обнаружили некоторые проблемы при использовании BeanPropertyRowMapper, и пришли к выводу, что мы не можем использовать его с записями.

Поддержи меня

Если вам нравится то, что вы только что прочитали, то вы можете купить мне кофе, перейдя по ссылке на изображении ниже:

Оригинал: “https://dev.to/yrashish/spring-boot-and-java-16-records-4pee”