Um Record, nada mais é que um tipo de classe que armazena dados. É a mesma ideia de construção similar a um JavaBean, possui construtor, atributos e métodos acessores. Porém, ao invés de possibilitar qualquer alteração a classe é imutável. Também possui os métodos equals, hashCode e toString().
Algumas boas vantagens a se considerar na utilização de records:
- Diminuir escrita de código boilerplate
- Tirar a necessidade de bibliotecas que fazem esse trabalho, como por exemplo, Lombok
- Imutabilidade
Um exemplo do que estamos falando
Imagine que tenha uma classe User
com nome e senha como atributos. Seria necessário essa implementação:
import java.util.Objects;
public class User {
private String name;
private String password;
public User(String name, String password) {
this.name = name;
this.password = password;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
User user = (User) o;
return name.equals(user.name) && password.equals(user.password);
}
@Override
public int hashCode() {
return Objects.hash(name, password);
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", password='" + password + '\'' +
'}';
}
}
Usando records, basta declarar:
public record User(String name, String password){}
Dado a contexto de imutabilidade, existe apenas um construtor com todos os atributos. Também não existem métodos setter.
User user = new User("admin", "45454*aa");
Outra diferença entre o primeiro design e este usando records é que os métodos acessores não usam a terminologia com "get" apenas o próprio nome do atributo, ou seja, ao invés de getName(), apenas name();
String name = user.name();
Usando records para serializar/desserializar classes
Dentre várias possibilidades usando records, uma utilidade seria serializar e desserializar classes que representam payloads de comunicação, também chamados de DTOs.
Testando o Jackson na versão 2.13.0 já é possível utilizar o recurso sem nenhuma configuração específica.
@Test
void shouldDeserializeCorrectly() throws Exception {
String json =
"""
{
"name": "willyancaetano",
"password": "jK%onD2B2Pvt"
}
""";
User user = objectMapper.readValue(json, User.class);
assertEquals("willyancaetano", user.name());
assertEquals("jK%onD2B2Pvt", user.password());
}
@Test
void shouldSerializeCorrectly() throws Exception {
User user = new User("admin", "$JJMwSYskkN^");
String json = objectMapper.writeValueAsString(user);
assertTrue(json.contains("admin"));
assertTrue(json.contains("$JJMwSYskkN^"));
}
E o que mais podemos fazer utilizando Records ?
Existem possibilidades de utilizar records em outros cenários, como por exemplo:
- Referências de registros de base de dados ou qualquer outro data-provider: Pense, registros que são buscados em um banco de dados podem ser records
- Mensagens enviadas e recebidas de comunicação assíncrona
E aí, o que achou dessa nova feature da linguagem ? Enxerga mais alguma outro cenário que seja possível utilizar records ?