0Pricing
Java Academy · Lekcja

Kompaktowe konstruktory i walidacja

Dodawaj logikę walidacji wewnątrz kompaktowych konstruktorów, aby zapewnić integralność danych.

Kompaktowe konstruktory i walidacja to bezpłatna lekcja Java Academy na CoddyKit. To lekcja 2 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.

Konstruktory kompaktowe

Konstruktor kompaktowy rekordu jest wykonywany przed przypisaniem komponentów. Umożliwia sprawdzanie lub normalizowanie danych bez powtarzania przypisań komponentów.

Konstruktor standardowy a kompaktowy

Standardowy konstruktor kanoniczny jawnie przypisuje komponenty. Konstruktor kompaktowy pomija listę parametrów i przypisania — są one wykonywane automatycznie po zakończeniu jego ciała.

// Standard canonical constructor
record Range(int min, int max) {
    Range(int min, int max) {
        if (min > max) throw new IllegalArgumentException("min > max");
        this.min = min;  // explicit assignment
        this.max = max;
    }
}

// Compact constructor (same behavior, less code)
record Range2(int min, int max) {
    Range2 {  // no parameter list, no assignments
        if (min > max) throw new IllegalArgumentException("min > max");
        // components are assigned automatically after this block
    }
}

Walidacja w konstruktorach kompaktowych

Konstruktory kompaktowe są standardowym sposobem walidowania danych rekordu. Należy wcześnie zgłaszać wyjątki, aby wymuszać zachowanie niezmienników.

record Email(String address) {
    Email {
        if (address == null || address.isBlank())
            throw new IllegalArgumentException("Email cannot be blank");
        if (!address.contains("@"))
            throw new IllegalArgumentException("Invalid email: " + address);
        address = address.toLowerCase().strip(); // normalize (Java 16+)
    }
}

Email e = new Email("  Alice@Example.com  ");
System.out.println(e.address()); // alice@example.com

try { new Email("not-an-email"); }
catch (IllegalArgumentException ex) { System.out.println(ex.getMessage()); }

Normalizacja w konstruktorach kompaktowych

W konstruktorze kompaktowym można modyfikować zmienne komponentów przed ich przypisaniem. Umożliwia to normalizowanie danych podczas tworzenia rekordu.

record PersonName(String firstName, String lastName) {
    PersonName {
        firstName = capitalize(firstName);
        lastName  = capitalize(lastName);
    }

    private static String capitalize(String s) {
        if (s == null || s.isEmpty()) return s;
        return Character.toUpperCase(s.charAt(0)) +
               s.substring(1).toLowerCase();
    }

    public String fullName() { return firstName + " " + lastName; }
}

PersonName name = new PersonName("jOHN", "DOE");
System.out.println(name.fullName()); // John Doe

Kopiowanie defensywne

W przypadku zmiennych komponentów, takich jak tablice lub kolekcje, należy wykonać kopię defensywną w konstruktorze kompaktowym, aby zachować niezmienność.

import java.util.*;

record Snapshot(List<String> items) {
    Snapshot {
        items = List.copyOf(items); // defensive copy — unmodifiable
    }
}

List<String> mutable = new ArrayList<>(List.of("a", "b", "c"));
Snapshot snap = new Snapshot(mutable);
mutable.add("d"); // doesn't affect snapshot
System.out.println(snap.items()); // [a, b, c]

try {
    snap.items().add("e"); // UnsupportedOperationException
} catch (UnsupportedOperationException e) {
    System.out.println("Snapshot is truly immutable!");
}

Rekord Range z ograniczeniami

Praktyczny rekord Range, który zapewnia spełnienie warunku min <= max i udostępnia przydatne metody pomocnicze.

record Range(int min, int max) {
    Range {
        if (min > max) throw new IllegalArgumentException(
            "min (" + min + ") must be <= max (" + max + ")");
    }

    public boolean contains(int value) { return value >= min && value <= max; }
    public int size()  { return max - min; }
    public int clamp(int value) { return Math.max(min, Math.min(max, value)); }
}

Range valid = new Range(1, 10);
System.out.println(valid.contains(5));  // true
System.out.println(valid.clamp(15));    // 10
System.out.println(valid.size());       // 9

Łączenie logiki konstruktora kompaktowego

W przypadku złożonej walidacji należy wydzielić metody pomocnicze i wywoływać je z konstruktora kompaktowego.

record CreditCard(String number, String cvv, int expiryMonth, int expiryYear) {
    CreditCard {
        validateNumber(number);
        validateCvv(cvv);
        validateExpiry(expiryMonth, expiryYear);
        number = number.replaceAll("[^0-9]", ""); // strip spaces/dashes
    }

    private static void validateNumber(String n) {
        String digits = n.replaceAll("[^0-9]", "");
        if (digits.length() < 13 || digits.length() > 19)
            throw new IllegalArgumentException("Invalid card number length");
    }
    private static void validateCvv(String cvv) {
        if (!cvv.matches("[0-9]{3,4}"))
            throw new IllegalArgumentException("Invalid CVV");
    }
    private static void validateExpiry(int m, int y) {
        if (m < 1 || m > 12) throw new IllegalArgumentException("Invalid month");
        if (y < 2024)         throw new IllegalArgumentException("Card expired");
    }
}

Wiele wzorców konstruktorów kompaktowych

Typowe wzorce walidacji używane w konstruktorach kompaktowych.

record Percentage(double value) {
    Percentage {
        if (value < 0 || value > 100)
            throw new IllegalArgumentException(
                "Percentage must be 0-100, got: " + value);
        value = Math.round(value * 100.0) / 100.0; // round to 2 dp
    }

    public double asFraction() { return value / 100.0; }
}

Percentage tax = new Percentage(8.756);
System.out.println(tax.value());       // 8.76
System.out.println(tax.asFraction()); // 0.0876

Konstruktory niekanoniczne

Rekordy mogą mieć dodatkowe konstruktory, ale muszą one delegować do konstruktora kanonicznego za pomocą this(...).

record Point(double x, double y) {
    // Non-canonical constructor: origin point
    Point() { this(0.0, 0.0); }

    // Non-canonical: polar coordinates
    static Point fromPolar(double r, double theta) {
        return new Point(r * Math.cos(theta), r * Math.sin(theta));
    }

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

Point origin = new Point();
Point p = Point.fromPolar(5, Math.PI/4);
System.out.printf("Distance: %.2f%n", origin.distance(p)); // 5.00

Rekord z niezmienną kolekcją

Rekord przechowujący niezmienny zrzut preferencji użytkownika, który podczas tworzenia rekordu sprawdza i normalizuje dane.

import java.util.*;

record UserPreferences(String theme, Set<String> enabledFeatures, int fontSize) {
    private static final Set<String> VALID_THEMES = Set.of("light", "dark", "system");
    private static final Set<String> VALID_FEATURES = Set.of("ai", "beta", "analytics");

    UserPreferences {
        if (!VALID_THEMES.contains(theme))
            throw new IllegalArgumentException("Unknown theme: " + theme);
        if (!VALID_FEATURES.containsAll(enabledFeatures))
            throw new IllegalArgumentException("Unknown feature in: " + enabledFeatures);
        if (fontSize < 10 || fontSize > 24)
            throw new IllegalArgumentException("fontSize must be 10-24");
        enabledFeatures = Set.copyOf(enabledFeatures); // defensive copy
    }
}

Ograniczenia konstruktorów kompaktowych

Czego nie można robić w konstruktorze kompaktowym:

  • Nie można jawnie przypisywać komponentów (są przypisywane automatycznie po zakończeniu bloku)
  • Nie można wywoływać this() ani super()
  • Rzucenie wyjątku zapobiega przypisaniu wszystkich komponentów
record Safe(int value) {
    Safe {
        // CAN: validate and modify component variables
        if (value < 0) value = 0; // normalized to 0 if negative
        // value = this.value; // NOT NEEDED — assignment happens after block
    }
}

System.out.println(new Safe(-5).value());  // 0 (normalized)
System.out.println(new Safe(10).value());  // 10

Szybkie sprawdzenie

Co dzieje się ze zmianami zmiennych komponentów wprowadzonymi w konstruktorze kompaktowym?

Podsumowanie: konstruktory kompaktowe i walidacja

Najważniejsze informacje:

  • Konstruktory kompaktowe są wykonywane przed przypisaniem komponentów — nie trzeba wykonywać jawnych przypisań
  • Używaj ich do walidowania, normalizowania lub defensywnego kopiowania komponentów
  • Modyfikuj zmienne komponentów, aby normalizować wartości (zamiana na małe litery, usunięcie białych znaków, kopiowanie)
  • Rzucaj IllegalArgumentException dla nieprawidłowych danych, aby wymuszać zachowanie niezmienników
  • Używaj List.copyOf / Set.copyOf dla zmiennych komponentów będących kolekcjami
  • Konstruktory niekanoniczne muszą delegować do kanonicznego za pomocą this(...)

Często zadawane pytania

Czy lekcja „Kompaktowe konstruktory i walidacja” jest bezpłatna?

Tak — pełny tekst „Kompaktowe konstruktory i walidacja” 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 „Kompaktowe konstruktory i walidacja”?

Dodawaj logikę walidacji wewnątrz kompaktowych konstruktorów, aby zapewnić integralność danych. Ć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 2 z 4.

Ile czasu zajmuje lekcja „Kompaktowe konstruktory i walidacja”?

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

  1. Wprowadzenie do rekordów
  2. Kompaktowe konstruktory i walidacja
  3. Własne metody w rekordach
  4. Rekordy a klasy i Lombok
← Powrót do Java Academy