TL;DR
Java Enum to nie tylko zestaw stałych – to pełnoprawne klasy! Mogą mieć konstruktory, metody, pola i implementować interfejsy. W tym artykule poznasz praktyczne zastosowania enum od podstawowych stałych po zaawansowane wzorce projektowe. Idealne dla developerów z 6-18 miesięcy doświadczenia w Java.
Dlaczego enum są kluczowe w nowoczesnej Javie
Wyobraź sobie kod z magicznymi stringami: „ACTIVE”, „INACTIVE”, „PENDING”. Jedna literówka i masz bug w produkcji. Albo gorzej – ktoś używa „active” zamiast „ACTIVE” i aplikacja się wiesza.
Java Enum rozwiązuje ten problem raz na zawsze. To type-safe sposób reprezentowania stałych wartości, który kompilator może zweryfikować. Ale w Javie enum to znacznie więcej niż proste stałe – to pełnoprawne klasy z supermocami.
Co się nauczysz z tego artykułu
- Tworzyć podstawowe enum i używać ich zamiast magicznych stałych
- Dodawać konstruktory, pola i metody do enum
- Implementować interfejsy w enum dla polimorfizmu
- Stosować enum w instrukcjach switch bezpiecznie
- Rozpoznawać kiedy użyć enum zamiast klas lub stałych
Wymagania wstępne
Poziom: Podstawy – 6-18 miesięcy programowania w Java
Potrzebujesz: Znajomość podstaw Java (klasy, metody, konstruktory), podstawy OOP, Java 8
Narzędzia: Java 8, IntelliJ IDEA 2017 lub Eclipse Oxygen
Od magicznych stringów do type-safe enum
Zacznijmy od problemu który enum rozwiązują:
// PROBLEM: Magiczne stringi - podatne na błędy public class OrderService { public void processOrder(String status) { if ("PENDING".equals(status)) { // logika dla oczekujących } else if ("COMPLETED".equals(status)) { // logika dla ukończonych } else if ("CANCELLED".equals(status)) { // logika dla anulowanych } // Co jeśli ktoś poda "pending" zamiast "PENDING"? } }
Teraz to samo z enum:
// ROZWIĄZANIE: Type-safe enum public enum OrderStatus { PENDING, COMPLETED, CANCELLED } public class OrderService { public void processOrder(OrderStatus status) { switch (status) { case PENDING: // logika dla oczekujących break; case COMPLETED: // logika dla ukończonych break; case CANCELLED: // logika dla anulowanych break; // Kompilator ostrzeże jeśli zabraknie case! } } }
Enum z konstruktorami i metodami
Prawdziwa moc enum ujawnia się gdy dodamy im zachowanie:
public enum Planet { MERCURY(3.303e+23, 2.4397e6), VENUS(4.869e+24, 6.0518e6), EARTH(5.976e+24, 6.37814e6), MARS(6.421e+23, 3.3972e6); private final double mass; // w kilogramach private final double radius; // w metrach // Konstruktor enum jest zawsze private! Planet(double mass, double radius) { this.mass = mass; this.radius = radius; } public double getMass() { return mass; } public double getRadius() { return radius; } // Metoda biznesowa public double calculateGravity() { final double G = 6.67300E-11; return G * mass / (radius * radius); } }
Używanie enum z metodami:
public class SpaceCalculator { public void calculateWeightOnPlanets(double earthWeight) { for (Planet planet : Planet.values()) { double weight = earthWeight * planet.calculateGravity() / Planet.EARTH.calculateGravity(); System.out.printf("Your weight on %s: %.2f kg%n", planet.name(), weight); } } }
Enum implementujące interfejsy
Enum mogą implementować interfejsy, co otwiera drzwi do polimorfizmu:
public interface Discountable { double applyDiscount(double price); } public enum CustomerType implements Discountable { REGULAR { @Override public double applyDiscount(double price) { return price; // brak zniżki } }, PREMIUM { @Override public double applyDiscount(double price) { return price * 0.9; // 10% zniżki } }, VIP { @Override public double applyDiscount(double price) { return price * 0.8; // 20% zniżki } }; // Możemy też dodać wspólne metody public String getDescription() { return "Customer type: " + this.name().toLowerCase(); } }
Praktyczne zastosowania enum
1. Konfiguracja aplikacji
public enum Environment { DEVELOPMENT("localhost:8080", "dev_db", true), STAGING("staging.company.com", "staging_db", true), PRODUCTION("api.company.com", "prod_db", false); private final String serverUrl; private final String database; private final boolean debugEnabled; Environment(String serverUrl, String database, boolean debugEnabled) { this.serverUrl = serverUrl; this.database = database; this.debugEnabled = debugEnabled; } public String getServerUrl() { return serverUrl; } public String getDatabase() { return database; } public boolean isDebugEnabled() { return debugEnabled; } }
2. State Machine Pattern
public enum TaskState { NEW { @Override public TaskState nextState() { return IN_PROGRESS; } }, IN_PROGRESS { @Override public TaskState nextState() { return COMPLETED; } }, COMPLETED { @Override public TaskState nextState() { throw new IllegalStateException("Task already completed"); } }; public abstract TaskState nextState(); }
Najlepsze praktyki enum
• Używaj WIELKICH_LITER dla nazw stałych
• Dodawaj pola final dla dodatkowych danych
• Implementuj toString() dla lepszego debugowania
• Używaj valueOf() ostrożnie – może rzucić IllegalArgumentException
• Zawsze dodawaj default case w switch dla bezpieczeństwa
public enum Priority { LOW(1, "Low Priority"), MEDIUM(2, "Medium Priority"), HIGH(3, "Critical Priority"); private final int level; private final String description; Priority(int level, String description) { this.level = level; this.description = description; } // Bezpieczne parsowanie public static Priority fromString(String name) { try { return valueOf(name.toUpperCase()); } catch (IllegalArgumentException e) { return LOW; // domyślna wartość } } @Override public String toString() { return description; } }
Używaj enum gdy masz skończony zestaw stałych wartości, które nie będą się zmieniać w runtime. Idealne do statusów, typów, kategorii, konfiguracji. Unikaj enum dla danych które mogą być dodawane dynamicznie.
Tak! Gdy zdefiniujesz metodę abstrakcyjną w enum, każda stała musi ją zaimplementować używając nawiasów klamrowych. To potężny wzorzec dla różnych zachowań każdej stałej.
Enum są bardzo wydajne – to singleton pattern wbudowany w język. Porównania są szybkie (==), zajmują mało pamięci, są thread-safe z natury. Znacznie lepsze od String constants.
Dodawanie na końcu enum jest bezpieczne. Uważaj tylko na switch statements – mogą nie obsługiwać nowych wartości. Zawsze dodawaj default case w switch dla bezpieczeństwa.
To specjalne kolekcje zoptymalizowane dla enum. EnumSet to wydajny Set dla enum, EnumMap to szybka mapa z kluczami typu enum. Używaj ich zamiast standardowych kolekcji gdy pracujesz z enum.
Przydatne zasoby
- Oracle Java Enum Tutorial
- Java 8 Enum API Documentation
- Baeldung Guide to Java Enums
- Effective Java – Joshua Bloch (Item 34: Use enums instead of int constants)
🚀 Zadanie dla Ciebie
Enum Challenge: Stwórz enum PaymentMethod z wartościami CREDIT_CARD, PAYPAL, BANK_TRANSFER. Każda metoda płatności ma prowizję (double) i maksymalną kwotę (BigDecimal). Dodaj metodę canProcess(BigDecimal amount) która sprawdza czy można przetworzyć daną kwotę.
Bonus: Zaimplementuj interfejs Processor z metodą process(BigDecimal amount) – każda metoda płatności ma inną logikę przetwarzania!
Jakich enum używasz najczęściej w swoich projektach? Może odkryłeś jakiś sprytny trick z enum? Podziel się swoimi doświadczeniami w komentarzach!