0Pricing
Java Academy · Lekcja

Metody abstrakcyjne w enumach

Zachowanie poszczególnych stałych

Metody abstrakcyjne w enumach 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.

Zachowanie poszczególnych stałych

Czasami każda stała enuma potrzebuje innego zachowania, a nie tylko innych danych.

Java pozwala zadeklarować w enumie metodę abstrakcyjną i nadpisać ją dla każdej stałej. Jest to wzorzec implementowania metod specyficznych dla poszczególnych stałych.

public class Main {
    enum Operation {
        PLUS { public int apply(int a, int b) { return a + b; } },
        MINUS { public int apply(int a, int b) { return a - b; } };
        public abstract int apply(int a, int b);
    }
    public static void main(String[] args) {
        System.out.println(Operation.PLUS.apply(3, 4));
        System.out.println(Operation.MINUS.apply(10, 6));
    }
}

Jak to działa

Każda stała z własnym ciałem staje się anonimową podklasą enuma.

Metoda abstrakcyjna wymusza dostarczenie implementacji przez każdą stałą, dzięki czemu kompilator gwarantuje kompletność.

public class Main {
    enum Operation {
        TIMES { public int apply(int a, int b) { return a * b; } };
        public abstract int apply(int a, int b);
    }
    public static void main(String[] args) {
        // The constant is an instance of a generated subclass
        System.out.println(Operation.TIMES.getClass().getSimpleName().isEmpty()
            ? "anonymous subclass" : Operation.TIMES.apply(6, 7));
    }
}

Pełny kalkulator

Cztery działania arytmetyczne występują jako stałe, a każda z nich ma własną implementację apply.

public class Main {
    enum Operation {
        PLUS  { public double apply(double a, double b) { return a + b; } },
        MINUS { public double apply(double a, double b) { return a - b; } },
        TIMES { public double apply(double a, double b) { return a * b; } },
        DIVIDE{ public double apply(double a, double b) { return a / b; } };
        public abstract double apply(double a, double b);
    }
    public static void main(String[] args) {
        double x = 6, y = 3;
        for (Operation op : Operation.values()) {
            System.out.printf("%s -> %.1f%n", op, op.apply(x, y));
        }
    }
}

Łączenie pól i zachowania

Można połączyć pole (symbol) z zachowaniem właściwym dla poszczególnych stałych. Pole opisuje działanie, a metoda je wykonuje.

public class Main {
    enum Operation {
        PLUS("+")  { public double apply(double a, double b) { return a + b; } },
        MINUS("-") { public double apply(double a, double b) { return a - b; } };
        private final String symbol;
        Operation(String symbol) { this.symbol = symbol; }
        public abstract double apply(double a, double b);
        @Override public String toString() { return symbol; }
    }
    public static void main(String[] args) {
        Operation op = Operation.PLUS;
        System.out.println("2 " + op + " 3 = " + op.apply(2, 3));
    }
}

Dlaczego nie użyć switch

Można zaimplementować apply za pomocą pojedynczego switch na stałej, ale podejście z metodą abstrakcyjną jest bezpieczniejsze.

Jeśli później doda się stałą, kompilator wymusi dostarczenie dla niej ciała metody. Instrukcja switch mogłaby po cichu pominąć nowy przypadek.

public class Main {
    enum Op {
        SQUARE { public int apply(int n) { return n * n; } },
        CUBE   { public int apply(int n) { return n * n * n; } };
        public abstract int apply(int n);
    }
    public static void main(String[] args) {
        System.out.println(Op.SQUARE.apply(5));
        System.out.println(Op.CUBE.apply(3));
    }
}

Metody konkretne obok abstrakcyjnych

Enum może łączyć metody abstrakcyjne i konkretne. Wspólną logikę należy umieścić w metodzie konkretnej, a zmienną logikę pozostawić abstrakcyjną.

public class Main {
    enum Shape {
        CIRCLE { public double area(double x) { return Math.PI * x * x; } },
        SQUARE { public double area(double x) { return x * x; } };
        public abstract double area(double x);
        public String report(double x) { return this + " area = " + area(x); }
    }
    public static void main(String[] args) {
        System.out.println(Shape.CIRCLE.report(2));
    }
}

Enumy strategii

Udoskonaleniem tego podejścia jest enum strategii: stałe delegują obsługę do zagnieżdżonego enuma strategii zamiast bezpośrednio nadpisywać metody.

Jest to przydatne, gdy kilka stałych współdzieli to samo zachowanie. W tym przykładzie dni robocze i weekendy różnią się sposobem obliczania wynagrodzenia.

public class Main {
    enum PayType {
        WEEKDAY { int overtime(int mins) { return mins; } },
        WEEKEND { int overtime(int mins) { return mins * 2; } };
        abstract int overtime(int mins);
        int pay(int mins) { return mins + overtime(Math.max(0, mins - 480)); }
    }
    public static void main(String[] args) {
        System.out.println("Weekend pay: " + PayType.WEEKEND.pay(540));
    }
}

Enum z interfejsem i ciałami stałych

Połączenie interfejsu z ciałami właściwymi dla poszczególnych stałych zapewnia maksymalną polimorficzność.

public class Main {
    interface Command { String run(); }
    enum Action implements Command {
        START { public String run() { return "starting..."; } },
        STOP  { public String run() { return "stopping..."; } };
    }
    public static void main(String[] args) {
        for (Action a : Action.values()) System.out.println(a.run());
    }
}

Dostęp do pól z poziomu ciał

Ciało właściwe dla stałej może odczytywać własne pola tej stałej, ponieważ każde ciało działa w kontekście danej instancji.

public class Main {
    enum Tax {
        STANDARD(0.20) { double apply(double amt) { return amt * (1 + rate); } },
        REDUCED(0.05)  { double apply(double amt) { return amt * (1 + rate); } };
        protected final double rate;
        Tax(double rate) { this.rate = rate; }
        abstract double apply(double amt);
    }
    public static void main(String[] args) {
        System.out.printf("%.2f%n", Tax.STANDARD.apply(100));
    }
}

Domyślnie, a potem nadpisanie

Można zdefiniować konkretne zachowanie domyślne w ciele enuma i nadpisać je tylko dla tych stałych, które się różnią.

public class Main {
    enum Notifier {
        EMAIL,
        SMS { String send(String m) { return "SMS: " + m; } };
        String send(String m) { return "EMAIL: " + m; }
    }
    public static void main(String[] args) {
        System.out.println(Notifier.EMAIL.send("hi"));
        System.out.println(Notifier.SMS.send("hi"));
    }
}

Miniaturowa maszyna stanów

Zachowanie właściwe dla poszczególnych stałych doskonale sprawdza się w maszynach stanów: każdy stan zna własne przejście.

public class Main {
    enum State {
        IDLE    { State next() { return RUNNING; } },
        RUNNING { State next() { return DONE; } },
        DONE    { State next() { return DONE; } };
        abstract State next();
    }
    public static void main(String[] args) {
        State s = State.IDLE;
        for (int i = 0; i < 3; i++) { System.out.println(s); s = s.next(); }
        System.out.println(s);
    }
}

Szybkie sprawdzenie

Proszę sprawdzić swoją wiedzę na temat abstrakcyjnych metod enumów.

Podsumowanie

Dowiedzieli się Państwo, jak definiować zachowanie poszczególnych stałych:

  • Należy zadeklarować metodę abstrakcyjną i nadpisać ją w ciele każdej stałej.
  • Każda stała staje się anonimową podklasą.
  • Kompilator wymusza kompletność implementacji, co jest bezpieczniejsze niż switch.
  • Pola, konkretne zachowanie domyślne i interfejsy można łączyć, tworząc rozbudowane enumy.

W następnej części: wydajny EnumSet.

public class Main {
    enum E { A { int v() { return 1; } }; abstract int v(); }
    public static void main(String[] args) {
        System.out.println("Abstract enum recap: " + E.A.v());
    }
}

Często zadawane pytania

Czy lekcja „Metody abstrakcyjne w enumach” jest bezpłatna?

Tak — pełny tekst „Metody abstrakcyjne w enumach” 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 „Metody abstrakcyjne w enumach”?

Zachowanie poszczególnych stałych Ć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 „Metody abstrakcyjne w enumach”?

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. Enummy z polami i metodami
  2. Metody abstrakcyjne w enumach
  3. EnumSet
  4. EnumMap
← Powrót do Java Academy