Java で列挙型を拡張する
このチュートリアルでは、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
を直接拡張することは不可能ですが、これらのトリックを使用して機能を拡張できます。
Sheeraz is a Doctorate fellow in Computer Science at Northwestern Polytechnical University, Xian, China. He has 7 years of Software Development experience in AI, Web, Database, and Desktop technologies. He writes tutorials in Java, PHP, Python, GoLang, R, etc., to help beginners learn the field of Computer Science.
LinkedIn Facebook