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

Java 14 Запись Ключевое слово

Изучите основы записей, включая их назначение, генерируемые методы и методы настройки.

Автор оригинала: Justin Albano.

1. Введение

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

До Java 14 это требовало создания класса с шаблонными полями и методами, которые были подвержены тривиальным ошибкам и запутанным намерениям.

С выпуском Java 14, теперь мы можем использовать записи для устранения этих проблем.

В этом учебнике Мы посмотрим на основы записей , включая их цель, методы и методы настройки .

2. Цель

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

Во многих случаях эти данные неизменяемые , с неизменяемость обеспечивает достоверность данных без синхронизации .

Для достижения этой цели мы создаем классы данных со следующими:

  1. частные , окончательный поле для каждой части данных
  2. getter для каждого поля
  3. общественные конструктор с соответствующим аргументом для каждого поля
  4. равняется метод, который возвращает истинное для объектов одного класса, когда все поля совпадают
  5. хэш-код метод, который возвращает одинаковое значение, когда все поля совпадают
  6. toString метод, который включает название класса и название каждого поля и его соответствующее значение

Например, мы можем создать простую Лицо класс данных с именем и адресом:

public class Person {

    private final String name;
    private final String address;

    public Person(String name, String address) {
        this.name = name;
        this.address = address;
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, address);
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        } else if (!(obj instanceof Person)) {
            return false;
        } else {
            Person other = (Person) obj;
            return Objects.equals(name, other.name)
              && Objects.equals(address, other.address);
        }
    }

    @Override
    public String toString() {
        return "Person [name=" + name + ", address=" + address + "]";
    }

    // standard getters
}

Хотя это достигает нашей цели, Есть две проблемы с ним:

  1. Существует много шаблонного кода
  2. Мы заслоняем цель нашего класса – представлять человека с именем и адресом

В первом случае мы должны повторить один и тот же утомительный процесс для каждого класса данных, монотонно создавая новое поле для каждой части данных, создавая равняется , хэш-код , и toString методы и создание конструктора, который принимает каждое поле.

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

Во втором случае дополнительный код скрывает, что наш класс является просто классом данных который имеет два Струнные поля: имя и адрес .

Лучшим подходом было бы прямо заявить, что наш класс является классом данных.

3. Основы

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

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

Создать Лицо запись, мы используем запись ключевое слово:

public record Person (String name, String address) {}

3.1. Конструктор

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

В случае нашей Лицо запись, эквивалентный конструктор:

public Person(String name, String address) {
    this.name = name;
    this.address = address;
}

Этот конструктор может быть использован так же, как класс для мгновенного использования объектов из записи:

Person person = new Person("John Doe", "100 Linda Ln.");

3.2. Геттеры

Мы также получаем публичные методы getters – чьи имена соответствуют названию нашего поля – бесплатно.

В нашем Лицо запись, это означает имя () и адрес () геттер:

@Test
public void givenValidNameAndAddress_whenGetNameAndAddress_thenExpectedValuesReturned() {
    String name = "John Doe";
    String address = "100 Linda Ln.";

    Person person = new Person(name, address);

    assertEquals(name, person.name());
    assertEquals(address, person.address());
}

3.3. равно

Кроме того, равняется метод генерируется для нас.

Этот метод возвращает истинный если поставленный объект имеет один и тот же тип и значения всех его полей совпадают:

@Test
public void givenSameNameAndAddress_whenEquals_thenPersonsEqual() {
    String name = "John Doe";
    String address = "100 Linda Ln.";

    Person person1 = new Person(name, address);
    Person person2 = new Person(name, address);

    assertTrue(person1.equals(person2));
}

Если какое-либо из полей отличается между двумя Лицо экземпляров, равняется метод вернет ложные .

3.4. хэш-код

Как и наши равняется метод, соответствующий хэш-код метод также генерируется для нас.

Наша хэш-код метод возвращает одинаковое значение в течение двух Лицо объекты, если все значения поля для обоих объектов совпадают (за исключением столкновений из- рождения ) :

@Test
public void givenSameNameAndAddress_whenHashCode_thenPersonsEqual() {
    String name = "John Doe";
    String address = "100 Linda Ln.";

    Person person1 = new Person(name, address);
    Person person2 = new Person(name, address);

    assertEquals(person1.hashCode(), person2.hashCode());
}

хэш-код значение будет отличаться, если какое-либо из значений поля будет отличаться.

3.5. toString

Наконец, мы также получаем toString метод, который приводит к строке, содержащей название записи, а затем имя каждого поля и его соответствующее значение в квадратных скобках .

Таким образом, мгновенное Лицо с именем “Джон Доу” и адрес “100 Линда Ln. ” результаты в следующий toString результат:

Person[name=John Doe, address=100 Linda Ln.]

4. Конструкторы

В то время как общественный конструктор генерируется для нас, мы все еще можем настроить нашу реализацию конструктора.

Эта настройка предназначена для проверки и должна быть как можно проще.

Например, мы можем гарантировать, что имя и адрес предоставлено нашим Лицо запись не нулевой с помощью следующей реализации конструктора:

public record Person(String name, String address) {
    public Person {
        Objects.requireNonNull(name);
        Objects.requireNonNull(address);
    }
}

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

public record Person(String name, String address) {
    public Person(String name) {
        this(name, "Unknown");
    }
}

Как и в случае со конструкторами классов, поля могут быть использованы с помощью этот ключевое слово (например, this.name и this.address ) и аргументы совпадают с названием полей (то есть, имя и адрес ).

Обратите внимание, что создание конструктора с теми же аргументами, что и генерируемый общественный конструктор, является действительным, но для этого требуется, чтобы каждое поле было инициализировано вручную :

public record Person(String name, String address) {
    public Person(String name, String address) {
        this.name = name;
        this.address = address;
    }
}

Кроме того, объявление конструктора без аргументов и со списком аргументов, соответствующим сгенерированному конструктору, приводит к ошибке компиляции .

Таким образом, следующее не будет компиляции:

public record Person(String name, String address) {
    public Person {
        Objects.requireNonNull(name);
        Objects.requireNonNull(address);
    }
    
    public Person(String name, String address) {
        this.name = name;
        this.address = address;
    }
}

5. Статические переменные и методы

Как и в обычных классах Java, мы также можем включить статические переменные и методы в наши записи .

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

public record Person(String name, String address) {
    public static String UNKNOWN_ADDRESS = "Unknown";
}

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

public record Person(String name, String address) {
    public static Person unnamed(String address) {
        return new Person("Unnamed", address);
    }
}

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

Person.UNKNOWN_ADDRESS
Person.unnamed("100 Linda Ln.");

6. Заключение

В этой статье мы рассмотрели запись ключевое слово, введено в Java 14, включая их фундаментальные концепции и тонкости.

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

Код и примеры для этого учебника можно найти более на GitHub .