CodeGym /Cursos /JAVA 25 SELF /Record: sintaxis y ventajas

Record: sintaxis y ventajas

JAVA 25 SELF
Nivel 22, Lección 0
Disponible

1. El problema de las «clases DTO»: ¿por qué necesitamos clases record?

Seamos sinceros: ¿cuántas veces has escrito una clase como esta?


public class Point {
    private final int x;
    private final int y;

    public Point(int x, int y) {
        this.x = x;
        this.y = y;
    }

    public int getX() {
        return x;
    }

    public int getY() {
        return y;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Point point = (Point) o;
        return x == point.x && y == point.y;
    }

    @Override
    public int hashCode() {
        return Objects.hash(x, y);
    }

    @Override
    public String toString() {
        return "Point{" + "x=" + x + ", y=" + y + '}';
    }
}

Parece sencillo, pero ¡mira cuántas líneas! Y ahora imagina que tienes 10 clases así, y en cada una 5–6 campos. Incluso la IDE se cansa de generar ese código repetitivo. Y si decides añadir un campo nuevo, tendrás que tocar el constructor, el getter, equals, hashCode, toString... Aburrido, rutinario y fuente de errores.

A estas clases se las llama DTO (Data Transfer Object) o Value Object. Simplemente almacenan datos — y ya. Pero por culpa del código plantilla resulta pesado mantenerlas.

Si te parece que esto no es un problema, espera a que tengas que modificar 50 de estas clases a la vez. ¡Entonces recordarás las clases record con especial cariño!

2. Introducción a record: sintaxis y la magia de Java 16+

Con Java 16 todo cambió. Apareció un nuevo tipo de clases — record. Están pensadas específicamente para los casos en los que solo hay que almacenar un conjunto de datos. La sintaxis se parece mucho a la de una tupla en otros lenguajes.

¿Cómo declarar un record?


public record Point(int x, int y) { }

¡Y... listo! Acabas de crear una clase inmutable con dos campos, constructor, getters, equals, hashCode y toString. Sin escribir de más.

¿Qué hace Java «bajo el capó»?

  • Campos x y y pasan a ser private final.
  • Getters se crean automáticamente: int x() y int y().
  • Constructor: public Point(int x, int y).
  • equals/hashCode: comparan todos los campos por valor.
  • toString: devuelve una cadena del tipo "Point[x=1, y=2]".

Se puede decir que un record es «un DTO con esteroides»: menos código, más garantías, menos bugs.

Inmutabilidad (immutability)

Todos los campos de una clase record son automáticamente final. Tras crear el objeto no se puede modificar — lo garantiza el compilador.

Si intentas añadir un setter o hacer que un campo no sea final, el compilador te detendrá. ¡No es frecuente encontrar tanta preocupación por tu tranquilidad!

3. Ejemplo de uso de una clase record

Clase normal (mucho código):


public class Client {
    private final String name;
    private final int id;

    public Client(String name, int id) {
        this.name = name;
        this.id = id;
    }

    public String getName() { return name; }
    public int getId() { return id; }

    // equals, hashCode, toString ...
}

Clase record (¡una sola línea!):


public record Client(String name, int id) { }

Uso:


public class Main {
    public static void main(String[] args) {
        Client client = new Client("Iván", 123);
        System.out.println(client.name()); // Iván
        System.out.println(client.id());   // 123
        System.out.println(client);        // Client[name=Iván, id=123]
    }
}

Ten en cuenta:
- Los métodos de acceso a los campos se llaman igual que los campos: name(), id().
- No hay setName() ni setId() — el objeto no se puede modificar tras su creación.

4. Ventajas de las clases record: menos código, menos errores

Menos código — más felicidad

¿Por qué escribir 40 líneas si puedes apañarte con una? Las clases record ahorran tiempo y nervios, especialmente en proyectos grandes con muchos DTO y Value Object.

Inmutabilidad «por contrato»

  • Las clases record son siempre final e inmutables.
  • No se puede falsificar un objeto ni cambiarlo accidentalmente.
  • No hay bugs «raros» por cambios de estado del objeto en lugares inesperados.
  • Se pueden usar con seguridad en programas concurrentes (si todos los campos también son inmutables).

Generación automática de equals/hashCode/toString

No hace falta escribir a mano métodos de comparación, cálculo del hash y representación. Todo se genera de forma automática y correcta.


Client c1 = new Client("Ana", 42);
Client c2 = new Client("Ana", 42);

System.out.println(c1.equals(c2));                 // true
System.out.println(c1.hashCode() == c2.hashCode()); // true
System.out.println(c1);                             // Client[name=Ana, id=42]

Ideal para colecciones y claves

Los objetos record pueden usarse como claves en HashMap, elementos en HashSet, etc. — todo funcionará correctamente porque equals y hashCode tienen en cuenta todos los campos.


import java.util.HashMap;
import java.util.Map;

Map<Client, String> clients = new HashMap<>();
clients.put(new Client("Ana", 42), "VIP");

System.out.println(clients.get(new Client("Ana", 42))); // VIP

Descripción explícita de los datos

La sintaxis de una clase record deja claro de inmediato qué datos se almacenan y que el objeto es inmutable. Esto hace que el código sea más comprensible para otros desarrolladores (y para ti dentro de seis meses).

5. Tabla: comparación entre una clase normal y una clase record

Clase normal Clase record
Sintaxis Mucho código Una línea
Inmutabilidad Hay que implementarla explícitamente Garantizada por el compilador
Autogeneración de métodos No Sí (equals, hashCode, toString)
Se pueden añadir campos Solo los componentes del record
Herencia Se puede heredar Siempre final, no se puede heredar
Uso en colecciones Hay que implementar correctamente los métodos Funciona de serie

6. Evolucionando la aplicación educativa: ejemplo con una clase record

Supongamos que en tu aplicación bancaria educativa necesitas guardar operaciones de cuenta: fecha, importe y tipo de operación (por ejemplo, «ingreso» o «retirada»).

Antes de Java 16:


public class Transaction {
    private final LocalDate date;
    private final double amount;
    private final String type;

    public Transaction(LocalDate date, double amount, String type) {
        this.date = date;
        this.amount = amount;
        this.type = type;
    }

    public LocalDate getDate() { return date; }
    public double getAmount() { return amount; }
    public String getType() { return type; }

    // equals, hashCode, toString ...
}

Con Java 16 y record:


import java.time.LocalDate;

public record Transaction(LocalDate date, double amount, String type) { }

Uso:


Transaction t = new Transaction(LocalDate.now(), 100.0, "deposit");
System.out.println(t);         // Transaction[date=2024-06-01, amount=100.0, type=deposit]
System.out.println(t.amount()); // 100.0

7. Visualización: qué genera un record

Veamos un record «desplegado» (aproximadamente, lo que generará el compilador):


public final class Point extends java.lang.Record {
    private final int x;
    private final int y;

    public Point(int x, int y) {
        this.x = x;
        this.y = y;
    }

    public int x() { return x; }
    public int y() { return y; }

    @Override
    public boolean equals(Object o) { /* comparación por campos */ }

    @Override
    public int hashCode() { /* cálculo por campos */ }

    @Override
    public String toString() { /* representación legible */ }
}

En breve: cuándo conviene usar record

  • Cuando necesitas un objeto inmutable con un conjunto de datos.
  • Cuando quieres un equals/hashCode/toString «honestos» sin escribirlos a mano.
  • Cuando haces DTO, Value Object, pares, tríos, color, punto, rango, clave para colecciones, etc.

8. Errores típicos al trabajar con clases record

Error №1: intentar añadir un setter o cambiar un campo tras la creación.
Una clase record no permite modificar sus campos. Si intentas añadir un método como setX(int x), el compilador te dirá enseguida «no se puede». Lo mismo ocurre si intentas cambiar un campo directamente.

Error №2: intentar añadir un campo no estático.
En una clase record solo se pueden declarar componentes (los campos entre paréntesis tras el nombre del record) y campos estáticos. No se pueden añadir campos no estáticos — el compilador no te dejará.

Error №3: usar record para lógica mutable.
Las clases record no están pensadas para objetos con estado cambiante. Si necesitas modificar algo después de crear el objeto, usa una clase normal.

Error №4: olvidar que un record es siempre final.
Una clase record no se puede heredar y no puede ser superclase. Intentar saltarse esta limitación provocará un error de compilación. Señal clave: no intentamos «extender» un record; está concebido como un tipo inmutable y cerrado.

Error №5: ignorar los métodos autogenerados.
Si sobrescribes equals, hashCode o toString, ten cuidado — no rompas su contrato, o de lo contrario las colecciones y las comparaciones funcionarán de forma incorrecta.

1
Tarea
JAVA 25 SELF, nivel 22, lección 0
Bloqueada
Creación de una ficha digital de libro 📚
Creación de una ficha digital de libro 📚
1
Tarea
JAVA 25 SELF, nivel 22, lección 0
Bloqueada
Visualización de datos del estudiante 🎓
Visualización de datos del estudiante 🎓
1
Tarea
JAVA 25 SELF, nivel 22, lección 0
Bloqueada
Verificación de identidad de ciudades en el mapa 🗺️
Verificación de identidad de ciudades en el mapa 🗺️
1
Tarea
JAVA 25 SELF, nivel 22, lección 0
Bloqueada
Gestión de acceso de usuarios en la aplicación 🔑
Gestión de acceso de usuarios en la aplicación 🔑
Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION