HOWTO · Java
Java で列挙型を拡張する
このチュートリアルでは、Java で enum 機能を拡張する方法を示します。
このチュートリアルでは、Java で enum 機能を拡張する方法を示します。
Java で enum を拡張する
enum は一種のコンパイラ マジックと見なすことができます。これは、byte コードでは、enum がいくつかの static メンバーを持つクラスとして表され、抽象 java.lang.Enum から継承されているためです。
これが、enum が他のクラスまたは enum を拡張できない理由です。 Java では enum を拡張できないため、どのクラスでも enum を拡張することはできません。 次のコード例を使用して学習し、何が起こるか見てみましょう。
コード例:
package delftstack;
enum Cars { Audi, BMW, Marcedes }
public class Example extends Cars {
public static void main(String... args) {}
}
上記のコードには Cars という名前の enum があり、クラス Example はそれを拡張しようとしています。 出力を参照してください:
/Example.java:5: error: cannot inherit from final Cars
public class Example extends Cars {
^
/Example.java:5: error: enum types are not extensible
public class Example extends Cars {
^
2 errors
ご覧のとおり、クラスは enum を拡張できません。 enum を拡張することが不可能な場合でも、その機能を拡張することはできますか?
機能は、enum で実装されたメソッドとして定義できます。 たとえば、上記のコードの enum Cars は、各メンバーの抽象メソッドを宣言できます。 例を参照してください。
enum Cars {
Audi {
@Override
public void drive() {}
},
BMW {
@Override
public void drive() {}
},
Mercedes {
@Override
public void drive() {}
},
;
public abstract void drive();
}
ご覧のとおり、各メンバーは drive() メソッドをオーバーライドできます。 しかし残念なことに、常に enum でメソッドを作成できるとは限りません:
enumがサードパーティ ライブラリまたは別のチームに属している場合、抽象メソッドの実装は許可されません。drive()メソッドに必要な依存関係を持たないモジュールに属している場合。enumが他の関数やデータでオーバーロードされている場合、読み取り不能になります。
これらの問題を解決し、Java の enum の機能を拡張できるソリューションがいくつか提供されています。
解決策 1: Java で enum をミラーリングする
名前が示すように、同じデータを持つ別の enum を作成する必要があります。 その新しい enum は drive() メソッドも実装するため、現在 2つの列挙型があります。
enum Cars のコード例:
enum Cars {
Audi {
@Override
public void drive() {}
},
BMW {
@Override
public void drive() {}
},
Mercedes {
@Override
public void drive() {}
},
;
public abstract void drive();
}
enum DriveCars のコード例:
enum DriveCars {
Audi {
@Override
public void drive() {}
},
BMW {
@Override
public void drive() {}
},
Mercedes {
@Override
public void drive() {}
},
;
public abstract void drive();
}
2 番目の enum は最初のもののミラーです。 機能を拡張することで、これらの列挙型の両方を使用できるようになりました。 name() と valueof() である組み込みの enum メソッドを使用する必要があります。
使用方法については、次の例を参照してください。
Cars cars = ... DriveCars.valueOf(cars.name()).drive();
上記のコードは、enum DriveCars で enum Cars 機能がどのように使用されるかを示しています。 つまり、機能が拡張されます。
上記のコードの name() メソッドは final であり、オーバーライドすることはできず、valueOf メソッドはコンパイラによって生成されます。 これらの方法は両方とも、拡張操作に機能上のエラーがない場合、互いにうまく適合します。
enum Cars が変更された場合、上記のコードには 1つの問題があり、enum DriveCars は何も認識せず、name と valueof トリックの失敗を引き起こします。 この問題を解決するには、親ミラーがCarsであることをDriveCarsに知らせる必要があります。
そのために、static イニシャライザを使用して DriveCars と Cars を比較できます。これは、両方の列挙型が一致しない場合に例外をスローします。 enumus ライブラリの例を次に示します。
enum DriveCars {
....static { Mirror.of(Cars.class); }
}
ユーティリティ クラスは、両方の列挙型が一致するかどうかをチェックします。 このメソッドは、name() および valueOf() トリックを検証します。
解決策 2: Java で enum をマップする
メソッドを 1つだけ保持する別の enum を作成したくない場合。 この場合、enum の代わりに interface を使用できます。 以下の例を参照してください。
public interface Driver {
void drive();
}
この interface Drive を enum Cars で使用するために、それらの間のマッピングを作成できます。 マップの例を次に示します。
Map<Cars, Driver> drivers = new EnumMap<>(Cars.class) {
{
put(Audi, new Driver() {
@Override
public void driver() {}
}) put(BMW, new Driver() {
@Override
public void driver() {}
}) put(Mercedes, new Driver() {
@Override
public void driver() {}
})
}
}
それらを使用するには、次の簡単なコードを使用します。
drivers.get(Cars).drive();
上記のコードで使用されている EnumMap は、各 enum メンバーが 1 回だけ表示されることを保証しますが、各メンバーのエントリは保証しません。
マップのサイズが列挙型のメンバーの数と等しいことを確認できます。
drivers.size() == Cars.values().length
enumus ライブラリは、この場合のユーティリティも提供します。マップが Cars に適合しない場合、IllegalStateException がスローされます。 ユーティリティは次のとおりです。
EnumMapValidator.validateValues(Cars.class, map, "Cars map");
上記の両方の方法は、機能を拡張して列挙型を強力にする方法を示しています。 enum を直接拡張することは不可能ですが、これらのトリックを使用して機能を拡張できます。