O Java 14 introduziu uma nova maneira de criar classes chamada Records. Neste tutorial, vamos aprender:
- Por que precisamos de Java Records
- Como criar Records e usá-los
- Sobreposição e extensão de classes Records
Leitura recomendada: Recursos do Java 14
Por que precisamos de Java Records?
Uma das reclamações comuns sobre o Java tem sido sua verbosidade. Se você precisar criar uma classe POJO simples, requer o seguinte código de boilerplate.
- Campos privados
- Métodos Getter e Setter
- Construtores
- Métodos hashCode(), equals() e toString()
Essa verbosidade é uma das razões para o alto interesse em Kotlin e Projeto Lombok.
Na verdade, a pura frustração de escrever esses métodos genéricos toda vez levou aos atalhos para criá-los em IDEs Java como Eclipse e IntelliJ IDEA.
Aqui está a captura de tela mostrando a opção da IDE Eclipse para gerar os métodos cerimoniais para uma classe.

Os Java Records são destinados a remover essa verbosidade, fornecendo uma estrutura compacta para criar as classes POJO.
Como Criar Registros em Java
Registros em Java são uma funcionalidade de prévia, desenvolvida sob JEP 359. Portanto, você precisa de duas coisas para criar Registros em seus projetos Java.
- JDK 14 instalado. Se estiver utilizando um IDE, ele também deve oferecer suporte ao Java 14. Tanto o Eclipse quanto o IntelliJ já fornecem suporte para o Java 14, então estamos bons aqui.
- Ativar Funcionalidade de Prévia: Por padrão, as funcionalidades de prévia estão desativadas. Você pode ativá-las no Eclipse nas configurações do compilador Java do projeto.

Você pode habilitar as funcionalidades de prévia do Java 14 na linha de comando usando a opção --enable-preview -source 14
.
Vamos supor que eu queira criar uma classe de modelo de funcionário. Ela ficará parecida com o código a seguir.
package com.journaldev.java14;
import java.util.Map;
public class Employee {
private int id;
private String name;
private long salary;
private Map<String, String> addresses;
public Employee(int id, String name, long salary, Map<String, String> addresses) {
super();
this.id = id;
this.name = name;
this.salary = salary;
this.addresses = addresses;
}
public int getId() {
return id;
}
public String getName() {
return name;
}
public long getSalary() {
return salary;
}
public Map<String, String> getAddresses() {
return addresses;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((addresses == null) ? 0 : addresses.hashCode());
result = prime * result + id;
result = prime * result + ((name == null) ? 0 : name.hashCode());
result = prime * result + (int) (salary ^ (salary >>> 32));
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Employee other = (Employee) obj;
if (addresses == null) {
if (other.addresses != null)
return false;
} else if (!addresses.equals(other.addresses))
return false;
if (id != other.id)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
if (salary != other.salary)
return false;
return true;
}
@Override
public String toString() {
return "Employee [id=" + id + ", name=" + name + ", salary=" + salary + ", addresses=" + addresses + "]";
}
}
Ufa, são mais de 70 linhas de código gerado automaticamente. Agora vamos ver como criar uma classe de Registro de Funcionário, que basicamente fornece as mesmas funcionalidades.
package com.journaldev.java14;
import java.util.Map;
public record EmpRecord(int id, String name, long salary, Map<String, String> addresses) {
}
Uau, isso não pode ficar mais curto que isso. Já estou adorando as classes de Registro.
Agora, vamos usar o comando javap
para descobrir o que está acontecendo nos bastidores quando um Registro é compilado.
# javac --enable-preview -source 14 EmpRecord.java
Note: EmpRecord.java uses preview language features.
Note: Recompile with -Xlint:preview for details.
# javap EmpRecord
Compiled from "EmpRecord.java"
public final class EmpRecord extends java.lang.Record {
public EmpRecord(int, java.lang.String, long, java.util.Map<java.lang.String, java.lang.String>);
public java.lang.String toString();
public final int hashCode();
public final boolean equals(java.lang.Object);
public int id();
public java.lang.String name();
public long salary();
public java.util.Map<java.lang.String, java.lang.String> addresses();
}
#

# javac —enable–preview –source 14 EmpRecord.java
# javap EmpRecord
Se você deseja mais detalhes internos, execute o comando javap com a opção -v.
- A Record class is final, so we can’t extend it.
- # javap -v EmpRecord
- Pontos importantes sobre as classes Record
- As classes Record estendem implicitamente a classe
java.lang.Record
. - A single constructor is created with all the fields specified in the record definition.
- Todos os campos especificados na declaração da classe Record são finais.
- Os campos da classe Record são imutáveis de forma “rasa” e dependem do tipo. Por exemplo, podemos alterar o campo de endereços acessando-o e, em seguida, fazendo atualizações.
A classe Record fornece automaticamente métodos de acesso para os campos. O nome do método é o mesmo que o nome do campo, diferentemente dos métodos getters genéricos e convencionais.
A classe Record fornece implementações de hashCode(), equals() e toString() também.
package com.journaldev.java14;
public class RecordTest {
public static void main(String[] args) {
EmpRecord empRecord1 = new EmpRecord(10, "Pankaj", 10000, null);
EmpRecord empRecord2 = new EmpRecord(10, "Pankaj", 10000, null);
Usando Registros em Programa Java
System.out.println(empRecord1);
Vamos dar uma olhada em um exemplo simples de uso da nossa classe EmpRecord.
System.out.println("Name: "+empRecord1.name());
System.out.println("ID: "+empRecord1.id());
// toString()
System.out.println(empRecord1.equals(empRecord2));
// acessando campos
System.out.println(empRecord1 == empRecord2);
}
}
// equals()
EmpRecord[id=10, name=Pankaj, salary=10000, addresses=null]
Name: Pankaj
ID: 10
true
false
// hashCode()
Output:
O objeto Record funciona da mesma maneira que qualquer classe modelo, objeto de dados, etc.
public record EmpRecord(int id, String name, long salary, Map<String, String> addresses) {
public EmpRecord {
if (id < 0)
throw new IllegalArgumentException("employee id can't be negative");
if (salary < 0)
throw new IllegalArgumentException("employee salary can't be negative");
}
}
Estendendo o Construtor de Registros
EmpRecord empRecord1 = new EmpRecord(-10, "Pankaj", 10000, null);
Às vezes, queremos ter algumas validações ou logs em nosso construtor. Por exemplo, o ID do funcionário e o salário não devem ser negativos. O construtor padrão não terá essa validação. Podemos criar um construtor compacto na classe de registro. O código deste construtor será colocado no início do construtor gerado automaticamente.
Exception in thread "main" java.lang.IllegalArgumentException: employee id can't be negative
at com.journaldev.java14.EmpRecord.<init>(EmpRecord.java:9)
Se criarmos um EmpRecord como no seguinte código:
Teremos uma exceção em tempo de execução:
public record EmpRecord(int id, String name, long salary, Map<String, String> addresses) {
public int getAddressCount() {
if (this.addresses != null)
return this.addresses().size();
else
return 0;
}
}
As Classes de Registros Podem Ter Métodos?
Sim, podemos criar métodos em registros.
No entanto, registros são destinados a serem transportadores de dados. Devemos evitar ter métodos utilitários em uma classe de registro. Por exemplo, o método acima pode ser criado em uma classe utilitária.
Se você acha que ter um método é essencial para sua classe de Registro, pense cuidadosamente se você realmente precisa de uma classe de Registro?
Source:
https://www.digitalocean.com/community/tutorials/java-records-class