Записи, пожалуй, самая обсуждаемая фича, появившаяся в Java 14. В то же время вызывает много критики из-за их необъектно-ориентированного характера. Типичный аргумент гласит, что записи — это концепция процедурного программирования, и ей нет места в объектно-ориентированном языке.

Действительно ли записи поощряют процедурное, а не объектное мышление?

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

Причина в том, что нет настоящих объектов на границах. Рассмотрим, например, ресурс REST, объект базы данных или свойства конфигурации. Это всего лишь структуры данных, но они должны быть представлены в коде Java.

Эволюция рекордов

Конечно, у нас есть типы структур данных, такие как Map или Set в Java, и их достаточно для многих случаев. Однако иногда полезно иметь типизированную структуру данных — например, для декларативной проверки, маршалинга или отображения. Традиционно Объекты передачи данных (DTO) используются в таких сценариях:

class AccountData {
 
  private String username;
  private String password;
  private String email;
     
  public AccountData(
      String username, String password, String email) {
    this.username = username;
    this.password = password;
    this.email = email;
  }
  public String getUsername() {
    return username;
  }
  public void setUsername(String username) {
    this.username = username;
  }
  public String getPassword() {
    return password;
  }
  public void setPassword(String password) {
    this.password = password;
  }
  public String getEmail() {
    return email;
  }
  public void setEmail(String email) {
    this.email = email;
  } 
  @Override
  public boolean equals(Object o) {
    if (this == o) {
      return true;
    }
    if (o == null || getClass() != o.getClass()) {
      return false;
    }
    AccountData that = (AccountData) o;
    return username.equals(that.username) &&
        password.equals(that.password) &&
        email.equals(that.email);
  }
  @Override
  public int hashCode() {
    return Objects.hash(
        username, password, email);
  }
}

Ну, это действительно много стандартного кода для такой простой вещи. Его можно ненавидеть, можно любить, но Ломбок довольно элегантно восполняет именно этот пробел:

@Data
class AccountData {
 
  private String username;
  private String password;
  private String email;
}

Lombok основан на манипуляциях с кодом, которые можно рассматривать как неадекватную «черную магию», которую многие разработчики не хотят иметь в своих кодовых базах. Java 14 решает эту проблему с помощью записей:

record AccountData (
    String username, String password, String email) {}

Любое сходство с классами данных Kotlin чисто случайно:

data class AccountData (
    var username: String, var password: String, var email: String)

Проблема с DTO

Итак, в чем здесь проблема? Записи Java, похоже, представляют DTO как языковую функцию. В чем проблема с DTO? "О" есть. Объекты передачи данных вообще не являются объектами! DTO — это просто типизированная структура данных.

Код должен быть как можно более явным, а структуры данных не должны называться объектами. Это заблуждение приводит к плохому проектированию объектов, обычно к печально известной анемичной модели предметной области.

С другой стороны, у записей (и даже у ломбокской аннотации @Data) нет объекта в имени. Записи делают это явным: данные рассматриваются как данные.

Вывод

Записи Java — это новая функция, которая добавляет в язык дополнительный тип структуры данных. В отличие от DTO, записи делают структуры данных в коде явными. Это правильные инструменты для правильных целей. При неправильном использовании они становятся злыми слугами, как и все остальное.

Я не думаю, что записи могут вызвать какой-либо сдвиг мышления от объектно-ориентированного дизайна к процедурному, потому что хороший объектно-ориентированный дизайн не рассматривает DTO как объекты, и записи не могут этого изменить. Простота использования сравнима с Lombok, но без магии; это означает, что искушение использовать записи не должно быть больше. Поскольку анемичное моделирование с DTO никогда не было хорошим объектно-ориентированным дизайном, я считаю, что записи не сделают его намного хуже.

В Java есть гораздо более опасные общие практики, такие как нулевые ссылки, статические методы и глобальные константы, но это совсем другая история…

Первоначально опубликовано в моем блоге.