JavaRush /Blog Java /Random-ES /Pausa para el café #230. ¿Qué son los Registros en Java y...

Pausa para el café #230. ¿Qué son los Registros en Java y cómo funcionan?

Publicado en el grupo Random-ES
Fuente: JavaTechOnline Este artículo proporciona una mirada en profundidad al concepto de Registros en Java con ejemplos, incluida su sintaxis, cómo crearlos y cómo usarlos. Pausa para el café #230.  Qué son los Registros en Java y cómo funcionan - 1Los registros en Java son una de las excelentes funciones que se introdujo por primera vez en Java 14 como una función de vista previa y finalmente apareció en la versión Java 17. Muchos desarrolladores lo utilizan activamente, lo que les ayuda a reducir con éxito una gran cantidad de código repetitivo. Además, gracias a los registros, no es necesario escribir una sola línea de código para que una clase sea inmutable.

¿Cuándo utilizar Record en Java?

Si desea pasar datos inmutables entre diferentes capas de su aplicación, utilizar Record puede ser una buena opción. De forma predeterminada, los registros en Java son inmutables, lo que significa que no podemos cambiar sus propiedades una vez creados. Como resultado, esto nos ayuda a evitar errores y mejora la confiabilidad del código. En pocas palabras, usando Record en Java, podemos generar clases automáticamente.

¿Dónde puedo usar Record en Java?

Generalmente, podemos usar registros en cualquier situación en la que necesitemos declarar contenedores de datos simples con propiedades inmutables y métodos generados automáticamente. Por ejemplo, a continuación se muestran algunos casos de uso en los que los registros pueden ser útiles: Objetos de transferencia de datos (DTO): podemos usar Record para declarar objetos de transferencia de datos simples que contienen datos. Esto es útil cuando se transfieren datos entre diferentes capas de aplicaciones, como entre la capa de servicio y la capa de base de datos. Objetos de configuración : el registro se puede utilizar para declarar objetos de configuración que contienen un conjunto de propiedades de configuración para una aplicación o módulo. Estos objetos suelen tener propiedades inmutables, lo que los hace seguros para subprocesos y fáciles de usar. Objetos de valor. El registro se puede utilizar para declarar objetos de valor que contienen un conjunto de valores que representan un concepto o modelo de dominio específico. Respuestas de API : al crear una API REST, los datos normalmente se devuelven en forma de JSON o XML. En tales casos, es posible que necesite definir una estructura de datos simple que represente la respuesta de la API. Los registros son ideales para esto porque le permiten definir una estructura de datos liviana e inmutable que se puede serializar fácilmente a JSON o XML. Datos de prueba. Al escribir pruebas unitarias, a menudo es necesario crear datos de prueba que representen un escenario específico. En tales casos, es posible que necesite definir una estructura de datos simple que represente los datos de prueba. Los registros pueden ser ideales para esto porque nos permiten definir una estructura de datos liviana e inmutable con un código repetitivo mínimo. Objetos tipo tupla : los registros se pueden utilizar para declarar objetos tipo tupla que contienen un número fijo de valores asociados. Esto puede resultar útil cuando se devuelven varios valores de un método o cuando se trabaja con colecciones de valores relacionados.

¿Qué es grabar en Java?

Record en Java es una clase diseñada para almacenar datos. Es similar a una clase tradicional de Java, pero es más ligera y tiene algunas características únicas. Los registros son inmutables de forma predeterminada, lo que significa que su estado no se puede cambiar una vez creados. Esto los hace ideales para almacenar datos inmutables, como parámetros de configuración o valores devueltos por una consulta de base de datos. También es una forma de crear un tipo de datos personalizado que consta de un conjunto de campos o variables, así como métodos para acceder y modificar esos campos. Record en Java facilita el trabajo con datos al reducir la cantidad de código repetitivo que los desarrolladores tienen que escribir una y otra vez. La sintaxis para crear una entrada en Java es:
record Record_Name(Fields....)

¿Cómo crear y utilizar Record en Java con ejemplos?

Veamos cómo crear y utilizar un registro en Java mediante programación.

Crear un registro

Crear un registro mediante programación no es muy similar a crear una clase normal en Java. En lugar de clase usamos la palabra clave record . También debe declarar campos con tipos de datos entre paréntesis del nombre del registro. Aquí hay un ejemplo de código que demuestra cómo crear una entrada en Java:
public record Book(String name, double price) { }
En este ejemplo, creamos un registro llamado Libro con dos campos: nombre y precio . La palabra clave pública indica que se puede acceder a esta entrada fuera del paquete en el que está definida.

Usando el registro

Para usar un registro en Java, podemos crear una instancia del mismo usando la nueva palabra clave, tal como lo hacemos con una clase normal. He aquí un ejemplo:
Book book = new Book( "Core Java" , 324.25);
Aquí se crea una nueva instancia de registro de Libro con el campo de nombre establecido en Core Java y el campo de precio establecido en 324,25 . Una vez que se crea un registro, se puede acceder a sus campos utilizando el nombre del propio campo. No existen métodos get ni set. En cambio, el nombre del campo se convierte en el nombre del método.
String name = book.name();

double price = book.price();
Aquí vemos el valor de los campos de nombre y precio que se recuperan del registro del Libro . Además de los campos, los registros también tienen algunos métodos integrados que puede utilizar para manipularlos. Por ejemplo, toString() , igual a() y hashCode() . Recuerde que los registros de Java son inmutables de forma predeterminada, lo que significa que una vez creados, su estado no se puede cambiar. Veamos otro ejemplo de cómo crear y utilizar registros en Java. Tomemos un caso en el que necesitamos un registro para almacenar información sobre un estudiante.
public record Student(String name, int age, String subject) {}
Esto crea un registro llamado Estudiante con tres campos: nombre , edad y materia . Podemos crear una instancia de esta entrada de la siguiente manera:
Student student = new Student("John Smith", 20, "Computer Science");
Luego podemos obtener los valores de los campos usando el nombre del campo:
String name = student.name();
int age = student.age();
String subject= student.subject();

¿Cómo se ve el registro después de la compilación?

Dado que Record es sólo un tipo especial de clase, el compilador también la convierte en una clase normal, pero con algunas restricciones y diferencias. Cuando el compilador convierte un registro (archivo Java) en código de bytes después del proceso de compilación, el archivo .class generado contiene algunas declaraciones adicionales de la clase Registro . Por ejemplo, a continuación se muestra el código de bytes generado para la entrada de Estudiante por el compilador de Java:
record Student(String name, int age) {   }

Escribir en un archivo .class (después de la compilación)

También podemos encontrar la siguiente clase convertida si usamos la herramienta javap y aplicamos el siguiente comando desde la línea de comando. Es importante tener en cuenta que la línea de comandos de Java incluye la herramienta javap , que puede utilizar para ver información sobre los campos, constructores y métodos de un archivo de clase. >javap Conclusión del estudiante: si revisamos el código de bytes cuidadosamente, es posible que tengamos algunas observaciones:
  • El compilador reemplazó la palabra clave Record con class .
  • El compilador declaró la clase final . Esto indica que esta clase no se puede ampliar. Esto también significa que no se puede heredar y es de naturaleza inmutable.
  • La clase convertida extiende java.lang.Record . Esto indica que todos los registros son una subclase de la clase Registro definida en el paquete java.lang .
  • El compilador agrega un constructor parametrizado.
  • El compilador generó automáticamente los métodos toString() , hashCode() y equals() .
  • El compilador ha agregado métodos para acceder a los campos. Preste atención a la convención de nomenclatura de los métodos: son exactamente iguales que los nombres de los campos, no debe haber un get o un set antes de los nombres de los campos .

Campos en registros

Los registros en Java definen su estado mediante un conjunto de campos, cada uno con un nombre y tipo diferente. Los campos de un registro se declaran en el encabezado del registro. Por ejemplo:
public record Person(String name, int age) {}
Esta entrada tiene dos campos: nombre de tipo String y edad de tipo int . Los campos de un registro son implícitamente definitivos y no se pueden reasignar una vez creado el registro. Podemos agregar un nuevo campo, pero esto no es recomendado. Un nuevo campo agregado a un registro debe ser estático. Por ejemplo:
public record Person(String name, int age) {

   static String sex;
}

Constructores en registros

Al igual que los constructores de clases tradicionales, los constructores de registros se utilizan para crear instancias de registros. Records tiene dos conceptos de constructores: el constructor canónico y el constructor compacto . El constructor compacto proporciona una forma más concisa de inicializar variables de estado en un registro, mientras que el constructor canónico proporciona una forma más tradicional con más flexibilidad.

constructor canónico

El compilador de Java predeterminado nos proporciona un constructor de todos los argumentos (constructor de todos los campos) que asigna sus argumentos a los campos correspondientes. Se le conoce como el constructor canónico. También podemos agregar lógica empresarial, como declaraciones condicionales, para validar los datos. A continuación se muestra un ejemplo:
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");
           }
      }
}

Diseñador compacto

Los constructores compactos ignoran todos los argumentos, incluidos los paréntesis. Los campos correspondientes se asignan automáticamente. Por ejemplo, el siguiente código demuestra el concepto de constructor compacto en Registros :
public record Person(String name, int age) {

      public Person {
          if (age < 18) {
              throw new IllegalArgumentException("You are not allowed to participate in general elections");
          }
     }
}
Además del constructor compacto, puedes definir constructores regulares en Record , como en una clase normal. Sin embargo, debe asegurarse de que todos los constructores inicialicen todos los campos del registro.

Métodos en registros

Los registros en Java generan automáticamente un conjunto de métodos para cada campo del registro. Estos métodos se denominan métodos de acceso y tienen el mismo nombre que el campo al que están asociados. Por ejemplo, si un registro tiene un campo llamado precio , automáticamente tendrá un método llamado precio() que devuelve el valor del campo precio . Además de los métodos de acceso generados automáticamente, también podemos definir nuestros propios métodos en una entrada, como en una clase normal. Por ejemplo:
public record Person(String name, int age) {
   public void sayHello() {
      System.out.println("Hello, my name is " + name);
   }
}
Esta entrada tiene un método sayHello() que imprime un saludo usando el campo de nombre .

Cómo ayuda Record a reducir el código repetitivo

Veamos un ejemplo sencillo para ver cómo las notaciones de Java pueden ayudar a eliminar el código repetitivo. Digamos que tenemos una clase llamada Persona que representa a una persona con nombre, edad y dirección de correo electrónico. Así es como definimos una clase de forma tradicional. Código sin 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 clase requiere una gran cantidad de código repetitivo para definir los campos, el constructor, los captadores, los definidores y el método toString() . Además, supongamos que queremos que esta clase sea inmutable. Para ello tendremos que hacer algunos pasos adicionales como:
  • Declarar la clase como final para que no pueda ampliarse.
  • Declare todos los campos privados y finales para que no se puedan cambiar fuera del constructor.
  • No proporcione ningún método de configuración para los campos.
  • Si alguno de los campos es mutable, debe devolver una copia del mismo en lugar de devolver el objeto original.
Código usando Registro :
public record Person(String name, int age, String email) {}
¡Eso es todo! Con solo una línea de código, hemos definido una clase que tiene los mismos campos, constructor, captadores, definidores y método toString() que una clase tradicional. La sintaxis Record se encarga de todo el código repetitivo por nosotros. Del ejemplo anterior, queda claro que el uso de registros en Java puede ayudar a eliminar el código repetitivo al proporcionar una sintaxis más concisa para definir clases con un conjunto fijo de campos. Se requiere menos código para definir y utilizar registros que con el enfoque tradicional, y los registros brindan acceso directo a los campos utilizando métodos para actualizar los campos. Esto hace que el código sea más legible, mantenible y menos propenso a errores. Además, con la sintaxis Record , no necesitamos hacer nada adicional para que la clase sea inmutable. De forma predeterminada, todos los campos de un registro son finales y la clase de registro en sí es inmutable. Por lo tanto, crear una clase inmutable usando sintaxis de escritura es mucho más simple y requiere menos código que el enfoque tradicional. Con los registros, no necesitamos declarar campos como finales , proporcionar un constructor que inicialice los campos ni proporcionar captadores para todos los campos.

Clases de registros comunes

Java también puede definir clases de registros genéricas . Una clase de entrada genérica es una clase de entrada que tiene uno o más parámetros de tipo. Aquí hay un ejemplo:
public record Pair<T, U>(T first, U second) {}
En este ejemplo , Pair es una clase de registro genérica que toma dos parámetros de tipo T y U. Podemos crear una instancia de este registro de la siguiente manera:
Pair<String, Integer>pair = new Pair<>( "Hello" , 123);

Clase anidada dentro de Record

También puede definir clases e interfaces anidadas dentro de una entrada. Esto es útil para agrupar clases e interfaces relacionadas y puede ayudar a mejorar la organización y el mantenimiento de su código base. A continuación se muestra un ejemplo de una entrada que contiene una clase anidada:
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;
       }
    }
}
En este ejemplo, Persona es una entrada que contiene una clase Contacto anidada . A su vez, Contacto es una clase anidada estática que contiene dos campos finales privados: dirección de correo electrónico y teléfono. También tiene un constructor que acepta un correo electrónico y un número de teléfono, y dos métodos getter: getEmail() y getPhone() . Podemos crear una instancia de Persona como esta:
Person person = new Person("John",30, new Person.Contact("john@example.com", "123-456-7890"));
En este ejemplo, creamos un nuevo objeto Persona llamado John , 30 años, y un nuevo objeto Contacto con correo electrónico john@example.com y teléfono 123-456-7890 .

Interfaz anidada dentro de Record

A continuación se muestra una entrada de ejemplo que contiene una interfaz anidada:
public record Book(String title, String author, Edition edition){
    public interface Edition{
       String getName();
   }
}
En este ejemplo, Libro es la entrada que contiene la interfaz Edición anidada . A su vez, Edition es una interfaz que define un único método getName() . Podemos crear una instancia de Libro de la siguiente manera:
Book book = new Book("The Hitchhiker's Guide to the Galaxy", "Douglas Adams", new Book.Edition() {

   public String getName() {

      return "Science Fiction";
   }
});
En este ejemplo, creamos un nuevo objeto Libro con el título La guía del autoestopista galáctico de Douglas Adams y una nueva implementación anónima de la interfaz Edición que devuelve el nombre Ciencia ficción cuando se llama al método getName() .

¿Qué más pueden hacer Registros?

  • Las entradas pueden definir constructores personalizados. Los registros admiten constructores parametrizados, que pueden llamar a un constructor predeterminado con parámetros proporcionados en sus cuerpos. Además, los registros también admiten constructores compactos, que son similares a los constructores predeterminados pero pueden incluir funciones adicionales como comprobaciones en el cuerpo del constructor.
  • Como cualquier otra clase en Java, Record puede definir y utilizar métodos de instancia. Esto significa que podemos crear y llamar a métodos específicos de la clase de grabación.
  • En Record , no se permite definir variables de instancia como miembros de clase porque solo se pueden especificar como parámetros del constructor. Sin embargo, los registros admiten campos estáticos y métodos estáticos, que se pueden utilizar para almacenar y acceder a datos comunes a todas las instancias de la clase de registro.

¿Puede un registro implementar interfaces?

Sí, escribir en Java puede implementar interfaces. Por ejemplo, el siguiente código demuestra el concepto:
public interface Printable {
   void print();
}
public record Person(String name, int age) implements Printable {
   public void print() {
      System.out.println("Name: " + name + ", Age: " + age);
   }
}
Aquí hemos definido una interfaz imprimible con un único método print() . También definimos una entrada Persona que implementa la interfaz Imprimible . El registro de Persona tiene dos campos: nombre y edad , y anula el método de impresión de la interfaz Imprimible para imprimir los valores de estos campos. Podemos crear una instancia de una entrada de Persona y llamar a su método de impresión de esta manera:
Person person = new Person("John", 30);
person.print();
Esto generará Nombre: John, Edad: 30 en la consola . Como se muestra en el ejemplo, los registros de Java pueden implementar interfaces como las clases normales. Esto puede resultar útil para agregar comportamiento a una entrada o para garantizar que una entrada se ajuste a un contrato definido por una interfaz.
Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION