JavaRush /Blogue Java /Random-PT /Pausa para café #230. O que são registros em Java e como ...

Pausa para café #230. O que são registros em Java e como funcionam?

Publicado no grupo Random-PT
Fonte: JavaTechOnline Este artigo fornece uma visão aprofundada do conceito de registros em Java com exemplos, incluindo sua sintaxe, como criá-los e como usá-los. Pausa para café #230.  O que são registros em Java e como funcionam - 1Registros em Java é um dos grandes recursos que foi introduzido pela primeira vez no Java 14 como um recurso de visualização e finalmente foi lançado no lançamento do Java 17. Muitos desenvolvedores o usam ativamente, o que os ajuda a reduzir com sucesso uma enorme quantidade de código clichê. Além disso, graças aos registros, você não precisa escrever uma única linha de código para tornar uma classe imutável.

Quando usar Record em Java?

Se você deseja passar dados imutáveis ​​entre diferentes camadas do seu aplicativo, usar Record pode ser uma boa escolha. Por padrão, os registros em Java são imutáveis, o que significa que não podemos alterar suas propriedades depois de criados. Como resultado, isso nos ajuda a evitar erros e melhora a confiabilidade do código. Simplificando, usando Record em Java, podemos gerar classes automaticamente.

Onde posso usar o Record em Java?

Geralmente, podemos usar registros em qualquer situação em que precisamos declarar contêineres de dados simples com propriedades imutáveis ​​e métodos gerados automaticamente. Por exemplo, abaixo estão alguns casos de uso onde os registros podem ser úteis: Objetos de transferência de dados (DTO): podemos usar Record para declarar objetos simples de transferência de dados que contêm dados. Isto é útil ao transferir dados entre diferentes camadas de aplicação, como entre a camada de serviço e a camada de banco de dados. Objetos de configuração : o registro pode ser usado para declarar objetos de configuração que contêm um conjunto de propriedades de configuração para um aplicativo ou módulo. Esses objetos normalmente têm propriedades imutáveis, tornando-os seguros para threads e fáceis de usar. Objetos de valor. O registro pode ser usado para declarar objetos de valor que contêm um conjunto de valores que representam um conceito específico ou modelo de domínio. Respostas da API : ao criar uma API REST, os dados normalmente são retornados na forma de JSON ou XML. Nesses casos, pode ser necessário definir uma estrutura de dados simples que represente a resposta da API. Os registros são ideais para isso porque permitem definir uma estrutura de dados leve e imutável que pode ser facilmente serializada em JSON ou XML. Dados de teste. Ao escrever testes unitários, muitas vezes você precisa criar dados de teste que representem um cenário específico. Nesses casos, pode ser necessário definir uma estrutura de dados simples que represente os dados de teste. Os registros podem ser ideais para isso porque nos permitem definir uma estrutura de dados leve e imutável com o mínimo de código padrão. Objetos semelhantes a tupla : os registros podem ser usados ​​para declarar objetos semelhantes a tupla que contêm um número fixo de valores associados. Isso pode ser útil ao retornar vários valores de um método ou ao trabalhar com coleções de valores relacionados.

O que é registro em Java?

Record em Java é uma classe projetada para armazenar dados. É semelhante a uma classe Java tradicional, mas é mais leve e possui alguns recursos exclusivos. Os registros são imutáveis ​​por padrão, o que significa que seu estado não pode ser alterado depois de criados. Isso os torna ideais para armazenar dados imutáveis, como parâmetros de configuração ou valores retornados de uma consulta ao banco de dados. É também uma forma de criar um tipo de dados personalizado que consiste em um conjunto de campos ou variáveis, bem como métodos para acessar e modificar esses campos. O registro em Java facilita o trabalho com dados, reduzindo a quantidade de código clichê que os desenvolvedores precisam escrever continuamente. A sintaxe para criar uma entrada em Java é:

record Record_Name(Fields....)

Como criar e usar Record em Java com exemplos?

Vamos ver como criar e usar um registro em Java programaticamente.

Criando um Registro

Criar um registro programaticamente não é muito semelhante à criação de uma classe regular em Java. Em vez de class usamos a palavra-chave record . Você também precisa declarar campos com tipos de dados entre parênteses do nome do registro. Aqui está um exemplo de código que demonstra como criar uma entrada em Java:

public record Book(String name, double price) { }
Neste exemplo, criamos um registro chamado Livro com dois campos: nome e preço . A palavra-chave public indica que esta entrada pode ser acessada fora do pacote no qual está definida.

Usando registro

Para usar um registro em Java, podemos instanciá-lo usando a palavra-chave new, assim como fazemos com uma classe normal. Aqui está um exemplo:

Book book = new Book( "Core Java" , 324.25);
Aqui, uma nova instância de registro de livro é criada com o campo de nome definido como Core Java e o campo de preço definido como 324,25 . Depois que um registro é criado, seus campos podem ser acessados ​​usando o nome do próprio campo. Não existem métodos get ou set. Em vez disso, o nome do campo torna-se o nome do método.

String name = book.name(); 

double price = book.price();
Aqui vemos o valor dos campos nome e preço sendo recuperados do registro Livro . Além dos campos, os registros também possuem alguns métodos integrados que você pode usar para manipulá-los. Por exemplo, toString() , equals() e hashCode() . Lembre-se de que os registros Java são imutáveis ​​por padrão, o que significa que, uma vez criados, seu estado não pode ser alterado. Vejamos outro exemplo de como criar e usar registros em Java. Vejamos um caso em que precisamos de um registro para armazenar informações sobre um aluno.

public record Student(String name, int age, String subject) {}
Isso cria um registro chamado Aluno com três campos: nome , idade e disciplina . Podemos criar uma instância desta entrada da seguinte maneira:

Student student = new Student("John Smith", 20, "Computer Science");
Podemos então obter os valores dos campos usando o nome do campo:

String name = student.name();
int age = student.age(); 
String subject= student.subject();

Como fica o Record após a compilação?

Como Record é apenas um tipo especial de classe, o compilador também a converte em uma classe regular, mas com algumas restrições e diferenças. Quando o compilador converte um registro (arquivo Java) em bytecode após o processo de compilação, o arquivo .class gerado contém algumas declarações adicionais da classe Record . Por exemplo, abaixo está o bytecode gerado para a entrada Aluno pelo compilador Java:

record Student(String name, int age) {   }

Gravando no arquivo .class (após compilação)

Também podemos encontrar a classe convertida abaixo se usarmos a ferramenta javap e aplicarmos o comando abaixo na linha de comando. É importante observar que a linha de comando Java inclui a ferramenta javap , que você pode usar para visualizar informações sobre os campos, construtores e métodos de um arquivo de classe. >javap Conclusão do aluno: Se verificarmos o bytecode com cuidado, podemos ter algumas observações:
  • O compilador substituiu a palavra-chave Record por class .
  • O compilador declarou a classe final . Isso indica que esta classe não pode ser estendida. Isso também significa que não pode ser herdado e é de natureza imutável.
  • A classe convertida estende java.lang.Record . Isso indica que todos os registros são uma subclasse da classe Record definida no pacote java.lang .
  • O compilador adiciona um construtor parametrizado.
  • O compilador gerou automaticamente os métodos toString() , hashCode() e equals() .
  • O compilador adicionou métodos para acessar campos. Preste atenção à convenção de nomenclatura dos métodos - eles são exatamente iguais aos nomes dos campos, não deve haver get ou set antes dos nomes dos campos .

Campos em registros

Os registros em Java definem seu estado usando um conjunto de campos, cada um com um nome e tipo diferente. Os campos de um registro são declarados no cabeçalho do registro. Por exemplo:

public record Person(String name, int age) {}
Esta entrada possui dois campos: nome do tipo String e idade do tipo int . Os campos de um registro são implicitamente finais e não podem ser reatribuídos após a criação do registro. Podemos adicionar um novo campo, mas isso não é recomendado. Um novo campo adicionado a um registro deve ser estático. Por exemplo:

public record Person(String name, int age) {

   static String sex;
}

Construtores em Registros

Assim como os construtores de classes tradicionais, os construtores de registros são usados ​​para criar instâncias de registros. Records possui dois conceitos de construtores: o construtor canônico e o construtor compacto . O construtor compacto fornece uma maneira mais concisa de inicializar variáveis ​​de estado em um registro, enquanto o construtor canônico fornece uma maneira mais tradicional com mais flexibilidade.

Construtor canônico

O compilador Java padrão nos fornece um construtor de todos os argumentos (construtor de todos os campos) que atribui seus argumentos aos campos correspondentes. É conhecido como construtor canônico. Também podemos adicionar lógica de negócios, como declarações condicionais, para validar os dados. Abaixo está um exemplo:

public record Person(String name, int age) {

       public Person(String name, int age) {
           if (age < 18) {
              throw new IllegalArgumentException("You are not allowed to participate in general elections");
           }
      }
}

Designer compacto

Os construtores compactos ignoram todos os argumentos, incluindo parênteses. Os campos correspondentes são atribuídos automaticamente. Por exemplo, o código a seguir demonstra o conceito de um construtor compacto em Records :

public record Person(String name, int age) {

      public Person {
          if (age < 18) {
              throw new IllegalArgumentException("You are not allowed to participate in general elections");
          }
     }
}
Além do construtor compacto, você pode definir construtores regulares em Record , assim como em uma classe normal. No entanto, você precisa ter certeza de que todos os construtores inicializam todos os campos do registro.

Métodos em registros

Os registros em Java geram automaticamente um conjunto de métodos para cada campo do registro. Esses métodos são chamados de métodos acessadores e possuem o mesmo nome do campo ao qual estão associados. Por exemplo, se um registro tiver um campo chamado price , então ele terá automaticamente um método chamado price() que retorna o valor do campo price . Além dos métodos acessadores gerados automaticamente, também podemos definir nossos próprios métodos em uma entrada, assim como em uma classe normal. Por exemplo:

public record Person(String name, int age) { 
   public void sayHello() { 
      System.out.println("Hello, my name is " + name); 
   }
}
Esta entrada possui um método sayHello() que imprime uma saudação usando o campo name .

Como o Record ajuda a reduzir o código padrão

Vejamos um exemplo simples para ver como as notações Java podem ajudar a eliminar o código padrão. Digamos que temos uma classe chamada Person que representa uma pessoa com nome, idade e endereço de email. É assim que definimos uma classe da maneira tradicional. Código sem usar Record :

public class Person {

    private String name;
    private int age;
    private String email;

    public Person(String name, int age, String email) {
        this.name = name;
        this.age = age;
        this.email = email;
    }

    public String getName() {

       return name;
    }

    public int getAge() {
       return age;
    }
    
    publicString getEmail() {
       return email;
    }

    public void setName(String name) {
       this.name = name;
    }

    public voidsetAge(int age) {
       this.age = age;
    }

    public void setEmail(String email) {
       this.email = email;
    }

    @Override
    public String toString() {
       return "Person{" +
         "name='" + name + '\'' +
         ", age=" + age +
         ", email='" + email + '\'' +
      '}';
    }
}
Como podemos ver, esta classe requer muito código padrão para definir os campos, construtor, getters, setters e método toString() . Além disso, suponha que queiramos tornar esta classe imutável. Para fazer isso, teremos que realizar algumas etapas adicionais, como:
  • Declare a classe como final para que ela não possa ser estendida.
  • Declare todos os campos como privados e finais para que não possam ser alterados fora do construtor.
  • Não forneça nenhum método setter para campos.
  • Se algum dos campos for mutável, você deverá retornar uma cópia dele em vez de retornar o objeto original.
Código usando registro :

public record Person(String name, int age, String email) {}
Isso é tudo! Com apenas uma linha de código, definimos uma classe que possui os mesmos campos, construtor, getters, setters e método toString() de uma classe tradicional. A sintaxe Record cuida de todo o código padrão para nós. A partir do exemplo acima, fica claro que o uso de registros em Java pode ajudar a eliminar o código clichê, fornecendo uma sintaxe mais concisa para definir classes com um conjunto fixo de campos. É necessário menos código para definir e usar registros do que a abordagem tradicional, e os registros fornecem acesso direto aos campos usando métodos para atualizar os campos. Isso torna o código mais legível, fácil de manter e menos sujeito a erros. Além disso, com a sintaxe Record , não precisamos fazer nada extra para tornar a classe imutável. Por padrão, todos os campos de um registro são finais e a própria classe do registro é imutável. Portanto, criar uma classe imutável usando sintaxe de gravação é muito mais simples e requer menos código do que a abordagem tradicional. Com registros, não precisamos declarar campos como final , fornecer um construtor que inicialize os campos ou fornecer getters para todos os campos.

Classes de registros comuns

Java também pode definir classes genéricas de Registros . Uma classe de entrada genérica é uma classe de entrada que possui um ou mais parâmetros de tipo. Aqui está um exemplo:

public record Pair<T, U>(T first, U second) {}
Neste exemplo , Pair é uma classe de registro genérica que utiliza dois parâmetros do tipo T e U. Podemos criar uma instância deste registro da seguinte maneira:

Pair<String, Integer>pair = new Pair<>( "Hello" , 123);

Classe aninhada dentro de Record

Você também pode definir classes e interfaces aninhadas em uma entrada. Isso é útil para agrupar classes e interfaces relacionadas e pode ajudar a melhorar a organização e a capacidade de manutenção da sua base de código. Aqui está um exemplo de uma entrada contendo uma classe aninhada:

public record Person(String name, int age, Contact contact){

    public static class Contact {

       private final String email;
       private final String phone; 
    
       public Contact(String email, String phone){
           this.email = email;
           this.phone = phone;
       }

       public String getEmail(){
          return email;
       }

       public String getPhone(){
          return phone;
       }
    }
}
Neste exemplo, Person é uma entrada que contém uma classe Contact aninhada . Por sua vez, Contact é uma classe estática aninhada que contém dois campos finais privados: endereço de e-mail e telefone. Ele também possui um construtor que aceita um e-mail e um número de telefone, e dois métodos getter: getEmail() e getPhone() . Podemos criar uma instância Person assim:

Person person = new Person("John",30, new Person.Contact("john@example.com", "123-456-7890"));
Neste exemplo, criamos um novo objeto Person chamado John , idade 30, e um novo objeto Contact com email john@example.com e telefone 123-456-7890 .

Interface aninhada dentro do Record

Aqui está um exemplo de entrada contendo uma interface aninhada:

public record Book(String title, String author, Edition edition){
    public interface Edition{
       String getName();
   }
}
Neste exemplo, Book é a entrada que contém a interface Edition aninhada . Por sua vez, Edition é uma interface que define um único método getName() . Podemos criar uma instância de Book da seguinte maneira:

Book book = new Book("The Hitchhiker's Guide to the Galaxy", "Douglas Adams", new Book.Edition() {

   public String getName() {

      return "Science Fiction";
   }
});
Neste exemplo, criamos um novo objeto Book com o título The Hitchhiker's Guide to the Galaxy de Douglas Adams e uma nova implementação anônima da interface Edition que retorna o nome Science Fiction quando o método getName() é chamado .

O que mais os Registros podem fazer?

  • As entradas podem definir construtores personalizados. Os registros suportam construtores parametrizados, que podem chamar um construtor padrão com parâmetros fornecidos em seus corpos. Além disso, os registros também suportam construtores compactos, que são semelhantes aos construtores padrão, mas podem incluir funcionalidades adicionais, como verificações no corpo do construtor.
  • Como qualquer outra classe em Java, Record pode definir e usar métodos de instância. Isso significa que podemos criar e chamar métodos específicos para a classe de gravação.
  • Em Record , definir variáveis ​​de instância como membros de classe não é permitido porque elas só podem ser especificadas como parâmetros de construtor. Entretanto, os registros suportam campos estáticos e métodos estáticos, que podem ser usados ​​para armazenar e acessar dados comuns a todas as instâncias da classe de registro.

Um registro pode implementar interfaces?

Sim, escrever em Java pode implementar interfaces. Por exemplo, o código abaixo demonstra o conceito:

public interface Printable {
   void print();
}

public record Person(String name, int age) implements Printable {
   public void print() {
      System.out.println("Name: " + name + ", Age: " + age);
   }
}
Aqui definimos uma interface imprimível com um único método print() . Também definimos uma entrada Person que implementa a interface Printable . O registro Person possui dois campos: name e age e substitui o método print da interface Printable para imprimir os valores desses campos. Podemos instanciar uma entrada Person e chamar seu método print assim:

Person person = new Person("John", 30); 
person.print();
Isso gerará Name: John, Age: 30 no console . Conforme mostrado no exemplo, os registros Java podem implementar interfaces como classes regulares. Isto pode ser útil para adicionar comportamento a uma entrada ou para garantir que uma entrada esteja em conformidade com um contrato definido por uma interface.
Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION