Własne metody w rekordach
Dodawaj metody instancji i statyczne metody fabrykujące, aby rozszerzać funkcjonalność rekordów.
Własne metody w rekordach to bezpłatna lekcja Java Academy na CoddyKit. To lekcja 3 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej Java Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs Java Academy zawiera 4 lekcji w sumie.
Niestandardowe metody w rekordach
Rekordy mogą zawierać metody instancji, metody statyczne i pola statyczne — dowolne zachowanie wynikające z komponentów rekordu lub z nimi związane.
Metody instancji w rekordach
Dodawaj metody do rekordów, aby obliczać wartości pochodne lub udostępniać przydatne operacje oparte na komponentach.
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.59Statyczne metody fabryczne
Statyczne metody fabryczne zapewniają nazwany i czytelny sposób tworzenia rekordów — ukrywają bezpośredni konstruktor, gdy jego użycie byłoby mniej jasne.
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());Metody predykatowe
Rekordy często udostępniają metody predykatowe zwracające wartość boolean, które sprawdzają warunki dotyczące przechowywanych danych.
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()); // trueMetody wither (copy-with)
Ponieważ rekordy są niezmienne, „aktualizacja” oznacza utworzenie nowego rekordu. Metody wither stosują wzorzec 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);Rekordy implementujące interfejsy funkcyjne
Rekord, który dodatkowo implementuje interfejs funkcyjny, można przekazywać bezpośrednio jako zamiennik lambdy.
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)Metody agregujące
Rekordy przechowujące kolekcje mogą udostępniać oparte na strumieniach metody agregujące.
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: 1600Niestandardowe przesłonięcie toString
Chociaż automatycznie generowana metoda toString jest przydatna podczas debugowania, można ją przesłonić na potrzeby prezentacji dla użytkownika.
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°FRekordy jako obiekty wartości w DDD
W projektowaniu sterowanym domeną rekordy naturalnie odwzorowują obiekty wartości — niezmienne typy definiowane przez swoje atrybuty, a nie tożsamość.
// 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);Porównywanie rekordów za pomocą Comparator
Rekordy można sortować za pomocą Comparator.comparing z referencjami do metod wskazującymi na metody dostępowe.
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()));Statyczne metody pomocnicze w rekordach
Rekordy mogą zawierać metody statyczne działające na wielu instancjach rekordów — przydatne podczas przekształcania i porównywania danych.
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));
}
}Szybkie sprawdzenie
Chcesz „zaktualizować” pole rekordu. Jakie podejście jest prawidłowe?
Podsumowanie: niestandardowe metody w rekordach
Najważniejsze informacje:
- Rekordy mogą zawierać metody instancji, metody statyczne i pola statyczne
- Dodawaj właściwości obliczane jako metody (area(), total(), display())
- Używaj statycznych metod fabrycznych jako czytelnych konstruktorów nazwanych
- Implementuj metody wither (withX) na potrzeby niezmiennych aktualizacji
- Przesłaniaj toString() na potrzeby prezentacji dla użytkownika zamiast danych debugowania
- Rekordy naturalnie odwzorowują obiekty wartości w projektowaniu sterowanym domeną
Często zadawane pytania
Czy lekcja „Własne metody w rekordach” jest bezpłatna?
Tak — pełny tekst „Własne metody w rekordach” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu Java Academy, przejdź na CoddyKit PRO. Kurs Java Academy zawiera 4 lekcji w sumie.
Co nauczysz się w „Własne metody w rekordach”?
Dodawaj metody instancji i statyczne metody fabrykujące, aby rozszerzać funkcjonalność rekordów. Ćwiczysz Java Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.
Czy potrzebuję doświadczenia, aby zacząć Java Academy?
Nie wymagamy żadnego doświadczenia. Java Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 3 z 4.
Ile czasu zajmuje lekcja „Własne metody w rekordach”?
Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.
Czy mogę pisać i uruchamiać kod w tej lekcji Java Academy?
Tak. Każda lekcja Java Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.
Wszystkie lekcje w tym kursie
- Wprowadzenie do rekordów
- Kompaktowe konstruktory i walidacja
- Własne metody w rekordach
- Rekordy a klasy i Lombok