0Pricing
Java Academy · Lección

Métodos personalizados en records

Añada métodos de instancia y métodos de fábrica estáticos para ampliar la funcionalidad de los records.

Métodos personalizados en records es una lección gratuita de Java Academy en CoddyKit. Esta es la lección 3 de 4. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de Java Academy, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de Java Academy incluye 4 lecciones en total.

Métodos personalizados en records

Los records pueden contener métodos de instancia, métodos estáticos y campos estáticos: cualquier comportamiento derivado de sus componentes o relacionado con ellos.

Métodos de instancia en records

Añada métodos a los records para calcular valores derivados o proporcionar operaciones útiles basadas en sus componentes.

record Money(long cents, String currency) {
    public double amount() { return cents / 100.0; }
    public String display() { return String.format("%s %.2f", currency, amount()); }
    public Money add(Money other) {
        if (!currency.equals(other.currency))
            throw new IllegalArgumentException("Currency mismatch");
        return new Money(cents + other.cents, currency);
    }
    public Money multiply(int factor) { return new Money(cents * factor, currency); }
}

Money price = new Money(1999, "USD");
Money tax = new Money(160, "USD");
System.out.println(price.add(tax).display()); // USD 21.59

Métodos de fábrica estáticos

Los métodos de fábrica estáticos ofrecen formas de construir records con nombres claros y legibles, ocultando el constructor directo cuando este resulta menos claro.

import java.time.*;

record DateRange(LocalDate start, LocalDate end) {
    DateRange { if (start.isAfter(end)) throw new IllegalArgumentException(); }

    public static DateRange of(LocalDate start, LocalDate end) {
        return new DateRange(start, end);
    }

    public static DateRange thisMonth() {
        LocalDate now = LocalDate.now();
        return new DateRange(now.withDayOfMonth(1),
            now.withDayOfMonth(now.lengthOfMonth()));
    }

    public long durationDays() {
        return ChronoUnit.DAYS.between(start, end) + 1;
    }
}

System.out.println(DateRange.thisMonth().durationDays());

Métodos predicado

Los records suelen exponer métodos predicado booleanos para comprobar condiciones de los datos que contienen.

record User(String email, String plan, boolean emailVerified) {
    public boolean isPro()      { return "PRO".equals(plan); }
    public boolean canPublish() { return isPro() && emailVerified; }
    public boolean isFree()     { return "FREE".equals(plan); }

    public User withPlan(String newPlan) {
        return new User(email, newPlan, emailVerified); // "update"
    }
}

User u = new User("alice@example.com", "FREE", true);
System.out.println(u.canPublish()); // false
User upgraded = u.withPlan("PRO");
System.out.println(upgraded.canPublish()); // true

Métodos wither (copy-with)

Como los records son inmutables, «actualizar» significa crear un record nuevo. Los métodos wither siguen el patrón withX(newValue).

record ServerConfig(String host, int port, boolean ssl, int timeout) {
    public ServerConfig withHost(String h)    { return new ServerConfig(h, port, ssl, timeout); }
    public ServerConfig withPort(int p)       { return new ServerConfig(host, p, ssl, timeout); }
    public ServerConfig withSsl(boolean s)    { return new ServerConfig(host, port, s, timeout); }
    public ServerConfig withTimeout(int t)    { return new ServerConfig(host, port, ssl, t); }
}

ServerConfig dev = new ServerConfig("localhost", 8080, false, 30);
ServerConfig prod = dev
    .withHost("api.example.com")
    .withPort(443)
    .withSsl(true)
    .withTimeout(60);
System.out.println(prod);

Records que implementan interfaces funcionales

Un record que también implementa una interfaz funcional puede pasarse directamente como sustituto de una lambda.

import java.util.function.*;

record Validator<T>(Predicate<T> rule, String message)
    implements Predicate<T> {

    public boolean test(T value) { return rule.test(value); }

    public String validate(T value) {
        return test(value) ? null : message;
    }
}

Validator<String> notEmpty = new Validator<>(
    s -> !s.isBlank(), "Must not be blank");
Validator<String> validEmail = new Validator<>(
    s -> s.contains("@"), "Invalid email");

System.out.println(notEmpty.validate(""));        // Must not be blank
System.out.println(validEmail.validate("a@b.c")); // null (valid)

Métodos de agregación

Los records que contienen colecciones pueden exponer métodos de agregación basados en streams.

import java.util.*;

record SalesReport(String region, List<Double> dailySales) {
    SalesReport { dailySales = List.copyOf(dailySales); }

    public double total()   { return dailySales.stream().mapToDouble(Double::doubleValue).sum(); }
    public double average() { return dailySales.stream().mapToDouble(Double::doubleValue).average().orElse(0); }
    public double max()     { return dailySales.stream().mapToDouble(Double::doubleValue).max().orElse(0); }
    public int    days()    { return dailySales.size(); }
}

SalesReport r = new SalesReport("North", List.of(1200.0, 1450.0, 980.0, 1600.0));
System.out.printf("Total: %.0f, Avg: %.0f, Max: %.0f%n", r.total(), r.average(), r.max());
// Total: 5230, Avg: 1307, Max: 1600

Sobrescritura personalizada de toString

Aunque el toString generado automáticamente resulta útil para la depuración, puede querer sobrescribirlo para mostrar información al usuario.

record Temperature(double celsius) {
    @Override
    public String toString() {
        return String.format("%.1f\u00b0C / %.1f\u00b0F",
            celsius, toFahrenheit());
    }

    public double toFahrenheit() { return celsius * 9.0/5.0 + 32; }
    public double toKelvin()     { return celsius + 273.15; }

    public static Temperature fromFahrenheit(double f) {
        return new Temperature((f - 32) * 5.0/9.0);
    }
}

Temperature boiling = new Temperature(100.0);
System.out.println(boiling); // 100.0°C / 212.0°F

Records como objetos de valor en DDD

En Domain-Driven Design, los records se corresponden naturalmente con los objetos de valor: tipos inmutables definidos por sus atributos, no por su identidad.

// Value objects in a banking domain
record AccountId(String value) {
    AccountId { if (!value.matches("ACC-[0-9]{8}")) throw new IllegalArgumentException(); }
}
record Amount(long cents, String currency) {
    public static Amount usd(long cents) { return new Amount(cents, "USD"); }
    public boolean isPositive() { return cents > 0; }
}
record TransactionId(java.util.UUID value) {
    public static TransactionId generate() {
        return new TransactionId(java.util.UUID.randomUUID());
    }
}

AccountId id = new AccountId("ACC-12345678");
Amount amount = Amount.usd(5000);
TransactionId txId = TransactionId.generate();
System.out.println(id + " " + amount + " " + txId);

Comparar records con Comparator

Los records pueden ordenarse mediante Comparator.comparing con referencias a métodos que apuntan a sus accessors.

import java.util.*;

record Employee(String name, String dept, double salary) {}

List<Employee> employees = List.of(
    new Employee("Alice",   "Engineering", 95000),
    new Employee("Bob",     "Marketing",   72000),
    new Employee("Charlie", "Engineering", 105000),
    new Employee("Diana",   "Marketing",   81000)
);

// Sort by dept, then salary descending
employees.stream()
    .sorted(Comparator.comparing(Employee::dept)
        .thenComparing(Comparator.comparingDouble(Employee::salary).reversed()))
    .forEach(e -> System.out.printf("%-10s %-15s %.0f%n",
        e.name(), e.dept(), e.salary()));

Métodos de utilidad estáticos en records

Los records pueden tener métodos estáticos que trabajen con varias instancias de records, lo que resulta útil para transformaciones y comparaciones.

import java.util.*;
import java.util.stream.*;

record Product(String id, String category, double price) {
    public static Map<String, DoubleSummaryStatistics> statsByCategory(
            List<Product> products) {
        return products.stream().collect(
            Collectors.groupingBy(Product::category,
                Collectors.summarizingDouble(Product::price)));
    }

    public static Optional<Product> cheapest(List<Product> products) {
        return products.stream()
            .min(Comparator.comparingDouble(Product::price));
    }
}

Comprobación rápida

Quiere «actualizar» un campo de un record. ¿Cuál es el enfoque correcto?

Repaso: métodos personalizados en records

Conclusiones principales:

  • Los records pueden tener métodos de instancia, métodos estáticos y campos estáticos
  • Añada propiedades calculadas como métodos (area(), total(), display())
  • Use métodos de fábrica estáticos para crear constructores con nombres legibles
  • Implemente métodos wither (withX) para realizar actualizaciones inmutables
  • Sobrescriba toString() para mostrar información al usuario en lugar de una salida de depuración
  • Los records se corresponden naturalmente con los objetos de valor de Domain-Driven Design

Preguntas frecuentes

¿La lección «Métodos personalizados en records» es gratis?

Sí — el texto completo de «Métodos personalizados en records» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de Java Academy, actualiza a CoddyKit PRO. El curso de Java Academy incluye 4 lecciones en total.

¿Qué aprenderé en «Métodos personalizados en records»?

Añada métodos de instancia y métodos de fábrica estáticos para ampliar la funcionalidad de los records. Practicas Java Academy con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.

¿Necesito experiencia previa para empezar Java Academy?

No se requiere experiencia previa. Java Academy en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 3 de 4.

¿Cuánto tiempo toma la lección «Métodos personalizados en records»?

La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.

¿Puedo escribir y ejecutar código en esta lección de Java Academy?

Sí. Cada lección de Java Academy incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.

Todas las lecciones de este curso

  1. Introducción a los records
  2. Constructores compactos y validación
  3. Métodos personalizados en records
  4. Records frente a clases y Lombok
← Volver a Java Academy