enum の抽象メソッド
定数ごとの振る舞いを定義します。
「enum の抽象メソッド」はCoddyKit上の無料Java Academyレッスンです。 これはレッスン2/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはJava Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Java Academyコースには全4レッスンが含まれています。
定数ごとの振る舞い
場合によっては、各列挙型定数に異なるデータだけでなく、異なる振る舞いも必要になります。
Javaでは、列挙型に抽象メソッドを宣言し、各定数でそれをオーバーライドできます。これは、定数固有のメソッド本体パターンです。
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));
}
}仕組み
本体を持つ各定数は、列挙型の匿名サブクラスになります。
抽象メソッドによってすべての定数に実装が必要になるため、コンパイラーが実装の完全性を保証します。
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));
}
}完全な電卓
4つの算術演算を定数として定義し、それぞれが独自の 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));
}
}
}フィールドと振る舞いの組み合わせ
フィールド(記号)と定数ごとの振る舞いを組み合わせられます。フィールドが演算をラベル付けし、メソッドが演算を実行します。
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));
}
}switch ではいけない理由
定数に対する単一の switch で apply を実装することもできますが、抽象メソッドを使う方法のほうが安全です。
後から定数を追加すると、コンパイラーによってその本体の実装を強制されます。switch では新しいケースを見落としても、気付かないままになる可能性があります。
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));
}
}抽象メソッドと具象メソッドの併用
列挙型では、抽象メソッドと具象メソッドを組み合わせられます。共通するロジックは具象メソッドに置き、変化するロジックは抽象メソッドに残します。
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));
}
}ストラテジー列挙型
発展的な方法としてストラテジー列挙型があります。定数が直接オーバーライドするのではなく、ネストしたストラテジー列挙型に処理を委譲します。
複数の定数で同じ振る舞いを共有する場合に便利です。この例では、平日と週末で給与の計算方法が異なります。
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));
}
}インターフェースと本体を持つ列挙型
インターフェースと定数固有の本体を組み合わせることで、最大限のポリモーフィズムを実現できます。
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());
}
}本体からのフィールドへのアクセス
定数固有の本体は、その定数自身のフィールドを読み取れます。各本体は、そのインスタンスのコンテキストで実行されるためです。
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));
}
}デフォルトとオーバーライド
列挙型本体に具象的なデフォルト実装を用意し、異なる定数に対してだけオーバーライドできます。
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"));
}
}小さなステートマシン
定数ごとの振る舞いは、ステートマシンで特に効果を発揮します。各状態が、自身の遷移を把握できます。
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);
}
}クイックチェック
列挙型の抽象メソッドについての理解度を確認しましょう。
まとめ
定数ごとの振る舞いについて学びました。
- 抽象メソッドを宣言し、各定数の本体でオーバーライドします。
- 各定数は匿名サブクラスになります。
- コンパイラーが実装の完全性を強制するため、switch よりも安全です。
- フィールド、具象的なデフォルト実装、インターフェースを組み合わせて、柔軟な列挙型を作れます。
次は、高性能な 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());
}
}よくある質問
「enum の抽象メソッド」レッスンは無料ですか?
はい。「enum の抽象メソッド」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Java Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Java Academyコースには全4レッスンが含まれています。
「enum の抽象メソッド」で何を学びますか?
定数ごとの振る舞いを定義します。 ブラウザで直接実行するハンズオンコードでJava Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。
Java Academyを始めるのに経験は必要ですか?
事前経験は必要ありません。CoddyKitのJava Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン2/4です。
「enum の抽象メソッド」レッスンにはどのくらい時間がかかりますか?
ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。
このJava Academyレッスンでコードを書いて実行できますか?
はい。すべてのJava Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。
このコースのすべてのレッスン
- フィールドとメソッドを持つ enum
- enum の抽象メソッド
- EnumSet
- EnumMap