Питання 8 · Розділ 20

Чи можна оголошувати статичні поля та методи в Record

Structured Java interview answer with junior, middle, and senior-level explanation.

Мовні версії: English Russian Ukrainian

🟢 Junior Level

Так, можна! Record підтримує static поля та методи. Це єдиний спосіб додати в Record щось окрім канонічних компонентів.

public record User(String name, int age) {
    // ✅ Статичне поле
    public static final int MAX_AGE = 150;

    // ✅ Статичний метод
    public static User anonymous() {
        return new User("Anonymous", 0);
    }
}

// Використання
User.MAX_AGE;  // 150
User.anonymous();  // User[name=Anonymous, age=0]

Не можна:

  • ❌ Instance поля (не static)
  • ✅ Тільки static поля

🟡 Middle Level

Як це працює

Static поля та методи:

public record Color(int r, int g, int b) {
    // ✅ Статичні константи
    public static final Color RED = new Color(255, 0, 0);
    public static final Color GREEN = new Color(0, 255, 0);
    public static final Color BLUE = new Color(0, 0, 255);

    // ✅ Статичний фабричний метод
    public static Color fromHex(String hex) {
        int r = Integer.parseInt(hex.substring(1, 3), 16);
        int g = Integer.parseInt(hex.substring(3, 5), 16);
        int b = Integer.parseInt(hex.substring(5, 7), 16);
        return new Color(r, g, b);
    }

    // ✅ Статичний лічильник
    private static final AtomicLong COUNTER = new AtomicLong(0);

    public Color {
        COUNTER.incrementAndGet();
    }

    public static long count() {
        return COUNTER.get();
    }
}

Типові помилки

  1. Спроба додати instance поле:
    public record User(String name) {
     // ❌ Instance поле — помилка компіляції
     private int mutableField = 0;
    
     // ✅ Тільки static
     private static int counter = 0;
    }
    
  2. Мутабельні static поля:
    public record Config() {
     // ⚠️ Небезпечно — mutable static state
     public static Map<String, String> settings = new HashMap<>();
    
     // ✅ Краще — immutable
     public static final Map<String, String> DEFAULTS = Map.of(
         "locale", "en",
         "timezone", "UTC"
     );
    }
    

Практичне застосування

1. Константи:

public record Money(BigDecimal amount, Currency currency) {
    public static final Money ZERO_USD = new Money(BigDecimal.ZERO, Currency.USD);
    public static final Money ZERO_EUR = new Money(BigDecimal.ZERO, Currency.EUR);
}

2. Фабричні методи:

public record Range(int min, int max) {
    public static Range fromString(String str) {
        String[] parts = str.split("-");
        return new Range(Integer.parseInt(parts[0]), Integer.parseInt(parts[1]));
    }

    public static Range of(int value) {
        return new Range(value, value);
    }
}

🔴 Senior Level

Internal Implementation

Static поля в Record:

// Static поля зберігаються в class, а не в instance
// Не впливають на:
// - Розмір екземпляра
// - equals() і hashCode() (не враховують static)
// - toString() (не включає static)
// - Серіалізацію

Обмеження:

  • Static поля не беруть участі в canonical конструкторі
  • Static методи не можуть звертатися до instance полів
  • Static ініціалізація відбувається при завантаженні класу

Архітектурні Trade-offs

Static поля vs окремі утиліти:

Підхід Плюси Мінуси
Static в Record Логіка поруч з даними Record стає «товстішим»
Окремий клас Чисте розділення Більше файлів

Edge Cases

1. Static ініціалізація з залежностями:

public record Currency(String code, String symbol) {
    public static final Map<String, Currency> REGISTRY;

    static {
        REGISTRY = Map.of(
            "USD", new Currency("USD", "$"),
            "EUR", new Currency("EUR", "€"),
            "GBP", new Currency("GBP", "£")
        );
    }

    public static Currency of(String code) {
        return REGISTRY.get(code.toUpperCase());
    }
}

2. Thread-safe static state:

public record IdGenerator(String prefix) {
    private static final AtomicLong SEQUENCE = new AtomicLong(0);

    public static String generate(IdGenerator gen) {
        return gen.prefix + "-" + SEQUENCE.incrementAndGet();
    }
}

Продуктивність

Static поля:
- Zero overhead на екземпляр
- Ініціалізація при завантаженні класу
- Thread-safe ініціалізація (JLS 12.4)

Production Experience

Константи та фабрики:

public record HttpStatus(int code, String reason) {
    public static final HttpStatus OK = new HttpStatus(200, "OK");
    public static final HttpStatus CREATED = new HttpStatus(201, "Created");
    public static final HttpStatus NOT_FOUND = new HttpStatus(404, "Not Found");
    public static final HttpStatus SERVER_ERROR = new HttpStatus(500, "Server Error");

    public static HttpStatus fromCode(int code) {
        return switch (code) {
            case 200 -> OK;
            case 201 -> CREATED;
            case 404 -> NOT_FOUND;
            case 500 -> SERVER_ERROR;
            default -> new HttpStatus(code, "Unknown");
        };
    }
}

Best Practices

// ✅ Static константи
public record Role(String name) {
    public static final Role ADMIN = new Role("ADMIN");
    public static final Role USER = new Role("USER");
    public static final Role GUEST = new Role("GUEST");
}

// ✅ Static фабрики
public record Pagination(int page, int size) {
    public static Pagination first() {
        return new Pagination(1, 20);
    }
}

// ❌ Mutable static state
// ❌ Static поля, які змінюються після ініціалізації

🎯 Шпаргалка для співбесіди

Обов’язково знати:

  • Record підтримує static поля та static методи — це єдиний спосіб додати щось окрім канонічних компонентів
  • Static поля не беруть участі в equals(), hashCode(), toString(), канонічному конструкторі
  • Static константи: public static final Money ZERO_USD = ...
  • Static фабричні методи: public static User anonymous() { ... }
  • Static лічильники через AtomicLong для відстеження створення екземплярів
  • Мутабельні static state — антипатерн (thread safety проблеми)

Часті уточнюючі запитання:

  • Чи можна додати instance поле в Record? — Ні, тільки static поля дозволені
  • Static поля впливають на equals/hashCode? — Ні, враховуються тільки канонічні компоненти
  • Чи можна створити Record з Registry патерном? — Так, static Map для зберігання екземплярів
  • Чи можна додати static блок ініціалізації? — Так, static { } блок працює як звичайно

Червоні прапорці (НЕ говорити):

  • ❌ “Static поля беруть участь в hashCode” — hashCode тільки від канонічних компонентів
  • ❌ “Instance поле можна додати як private” — Будь-яке instance поле — compilation error
  • ❌ “Mutable static state — гарна ідея” — Thread safety і передбачуваність страждають
  • ❌ “Static метод може звертатися до instance полів” — Static контекст не має доступу до instance

Пов’язані теми:

  • [[1. Що таке Record в Java і з якої версії вони доступні]]
  • [[4. Чи можна додавати додаткові методи в Record]]
  • [[6. Чи можна перевизначити конструктор в Record]]
  • [[9. Чи є поля Record фінальними]]