CodeGym /Cursos /JAVA 25 SELF /Record con métodos

Record con métodos

JAVA 25 SELF
Nivel 22 , Lección 3
Disponible

1. Ampliación de un record: métodos adicionales

¿Se pueden añadir métodos a un record?

¡Por supuesto! Un Record es como un piso ya reformado: las paredes y el suelo ya están hechos, no puedes cambiarlos, pero nadie te impide colocar los muebles a tu gusto. Dentro de un record puedes declarar métodos normales, métodos estáticos e incluso guardar constantes. Esto significa que la lógica de negocio no tiene por qué ir a clases «utilitarias» aparte: se puede integrar cuidadosamente en el propio record.

Ejemplo: método para calcular la distancia entre puntos

Supongamos que tenemos un record para un punto en el plano:


public record Point(int x, int y) {
    // Método adicional
    public double distanceTo(Point other) {
        int dx = this.x - other.x;
        int dy = this.y - other.y;
        return Math.sqrt(dx * dx + dy * dy);
    }
}

Ahora podemos hacer esto:


Point p1 = new Point(0, 0);
Point p2 = new Point(3, 4);
System.out.println(p1.distanceTo(p2)); // 5.0

Como ves, un record se puede «enriquecer» con sus propios métodos — ¡y es muy cómodo!

Ejemplo: método estático


public record Rectangle(int width, int height) {
    public int area() {
        return width * height;
    }

    public static Rectangle square(int size) {
        return new Rectangle(size, size);
    }
}

Ahora se puede crear un «cuadrado» con una sola llamada:


Rectangle r = Rectangle.square(5);
System.out.println(r.area()); // 25

2. Constructor compacto y validación de datos

¿Para qué sirve el constructor «compacto»?

El constructor canónico de un record se crea automáticamente y asigna los parámetros a los campos. Pero a veces queremos añadir comprobación de datos de entrada (por ejemplo, prohibir coordenadas negativas).

En una clase normal escribiríamos:


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

    public Point(int x, int y) {
        if (x < 0 || y < 0) throw new IllegalArgumentException();
        this.x = x;
        this.y = y;
    }
    // ...
}

En un record se puede declarar un constructor compacto — sin repetir la lista de parámetros y sin asignar explícitamente los campos (eso lo hace el compilador por nosotros).

Sintaxis del constructor compacto


public record Point(int x, int y) {
    public Point {
        if (x < 0 || y < 0) {
            throw new IllegalArgumentException("Coordinates must be non-negative");
        }
        // No hace falta escribir: this.x = x; this.y = y;
    }
}
  • Los parámetros del constructor coinciden automáticamente con los componentes del record.
  • La asignación this.x = x y this.y = y la realiza el compilador automáticamente tras ejecutar el cuerpo del constructor (o tras salir de él con éxito).
  • Si se lanza una excepción, el objeto no se creará.

Ejemplo con comprobación


Point p1 = new Point(3, 5); // OK
Point p2 = new Point(-1, 2); // ¡Lanzará IllegalArgumentException!

¿Se puede declarar un constructor «normal»?

Sí. Si necesitas crear un constructor con otra lista de parámetros o con lógica adicional, decláralo explícitamente:


public record Range(int from, int to) {
    public Range(int size) {
        this(0, size); // llama al constructor principal
    }
}

3. Limitaciones de los records

A diferencia de las clases normales, los record tienen una serie de limitaciones. Es importante tenerlo en cuenta para no sorprenderse con errores de compilación.

Solo componentes — ningún campo adicional no estático

En un record no se pueden declarar nuevos campos no estáticos:


public record Person(String name, int age) {
    // int id; // Error de compilación. No se pueden añadir campos no estáticos.
}

Se pueden declarar campos y métodos estáticos:


public record Person(String name, int age) {
    public static final String SPECIES = "Homo sapiens";
}

Un record siempre es final

Un Record no puede ser clase padre (no se puede heredar de él) y no puede heredar explícitamente de otra clase (salvo de la herencia implícita de java.lang.Record). Esto significa que un record es siempre una estructura «final».


public record User(String login) { }

// public class Admin extends User {} // Error: no se puede heredar de un record.

Se pueden implementar interfaces

Un Record puede implementar interfaces:


public interface Printable {
    void print();
}

public record Invoice(int amount) implements Printable {
    @Override
    public void print() {
        System.out.println("Importe: " + amount);
    }
}

4. Ejemplos: records enriquecidos en tareas reales

Veamos algunos ejemplos prácticos donde los métodos adicionales y los constructores compactos hacen que un record sea realmente útil.

Record con método calculado


public record Circle(double x, double y, double radius) {
    public double area() {
        return Math.PI * radius * radius;
    }

    public double distanceTo(Circle other) {
        double dx = x - other.x;
        double dy = y - other.y;
        return Math.sqrt(dx * dx + dy * dy);
    }
}

Record con validación


public record Email(String value) {
    public Email {
        if (value == null || !value.contains("@")) {
            throw new IllegalArgumentException("Email no válido: " + value);
        }
    }
}

Ahora no se puede crear un email no válido:


Email e1 = new Email("test@example.com"); // OK
Email e2 = new Email("not-an-email"); // Lanzará IllegalArgumentException

Record con métodos estáticos adicionales


public record Temperature(double celsius) {
    public static Temperature fromFahrenheit(double fahrenheit) {
        return new Temperature((fahrenheit - 32) * 5 / 9);
    }

    public double toFahrenheit() {
        return celsius * 9 / 5 + 32;
    }
}

Uso:


Temperature t = Temperature.fromFahrenheit(98.6);
System.out.println(t.celsius()); // 37.0
System.out.println(t.toFahrenheit()); // 98.6

5. Constructor compacto: matices y limitaciones

¿Cuándo usar el constructor compacto?

  • Si necesitas comprobar la validez de los datos (validación).
  • Si necesitas cambiar valores antes de almacenarlos (por ejemplo, redondear un número o convertir una cadena a mayúsculas).
  • Si quieres evitar duplicar la lista de parámetros.

Detalles de funcionamiento

  • En el constructor compacto no se pueden asignar explícitamente valores a los componentes (this.x = ...) — esto provocará un error de compilación, ya que el compilador realiza la asignación tras ejecutar el cuerpo del constructor.
  • En el constructor compacto no se pueden cambiar los nombres de los parámetros: siempre coinciden con los nombres de los componentes del record.

Ejemplo: redondeo automático


public record Money(double amount) {
    public Money {
        amount = Math.round(amount * 100) / 100.0; // Redondeamos a 2 decimales
    }
}

6. Práctica: mejoramos la aplicación didáctica

Supongamos que estamos implementando operaciones bancarias en una aplicación didáctica. Tenemos un record Transaction, que guarda el importe, el remitente y el destinatario.


public record Transaction(String from, String to, double amount) {
    public Transaction {
        if (amount <= 0) throw new IllegalArgumentException("El importe debe ser positivo");
        if (from == null || to == null) throw new IllegalArgumentException("Los campos no pueden ser null");
    }

    public String description() {
        return String.format("Transferencia %.2f de %s a %s", amount, from, to);
    }
}

Uso:


Transaction t = new Transaction("Alice", "Bob", 150.0);
System.out.println(t.description()); // Transferencia 150.00 de Alice a Bob

Intentar crear una transacción no válida provocará un error:


Transaction t2 = new Transaction("Alice", "Bob", -10.0); // IllegalArgumentException

Tabla: qué se puede y qué no se puede en un record

Se permite en un record No se permite en un record
Métodos normales Nuevos campos no estáticos
Métodos y campos estáticos Extender otras clases
Implementar interfaces Ser superclase de otros
Constructores compactos y normales Modificar componentes después de crear el objeto
Sobrescribir métodos Usar setters

7. Errores típicos al trabajar con records con cuerpo no estándar

Error n.º 1: intentar añadir un campo no estático.
Los principiantes a menudo intentan añadir a un record «otro campo para la lógica interna» — por ejemplo, un contador o una caché. Esto no funcionará: el compilador dará un error de inmediato. Si necesitas almacenar estado adicional, probablemente un record no sea tu elección.

Error n.º 2: olvidar la validación en el constructor compacto.
Si quieres que el objeto sea siempre válido, realiza la comprobación en el constructor compacto. No confíes en que «el usuario no introducirá disparates».

Error n.º 3: intentar modificar un componente después de crear el objeto.
Los campos de un record son final — no se pueden modificar ni directamente ni a través de métodos. Si necesitas una estructura mutable, usa una clase normal.

Error n.º 4: duplicar lógica en métodos y en el constructor.
A veces se intenta duplicar la lógica de comprobación y la de cálculo tanto en métodos como en el constructor. Es mejor hacer toda la validación en el constructor y dejar los métodos para la lógica de negocio «limpia».

Error n.º 5: olvidar las limitaciones de herencia.
Un Record siempre es final — no se puede crear un descendiente de él. Si estás diseñando una jerarquía donde se necesitan subclases, usa clases normales.

Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION