最近、シングルトン設計パターン、それを Java で実装する方法、およびその目的について詳しく調べました。しかし、Java にはすぐに独自のシングルトンが付属していると言ったらどうなるでしょうか? 興味をそそられましたか? それでは、飛び込んでみましょう。
おそらく、Enum クラスについてはすでにご存知でしょう。知っておくべき特別な機能があります。具体的には、Enum はシングルトン設計パターンを実装します。このオプションは、パブリックフィールドを含むシングルトン アプローチとほぼ同じです。
列挙型としてのシングルトン:
public enum Device {
PRINTER
}
パブリック変数としてのシングルトン:
public class Printer {
public static final Printer PRINTER = new Printer();
private Printer() {
}
//…
}
enumアプローチは、独自の実装を記述する必要がないため、パブリックフィールド アプローチよりもコンパクトです。最も重要なことは、列挙型ではシリアル化に問題がないことです。
enum のシリアル化は、通常のオブジェクトの場合とは動作が異なります。enum 名の値のみがシリアル化されます。逆シリアル化中、このメソッドは逆シリアル化された名前とともに使用され、インスタンスを取得します。さらに、enum はリフレクション攻撃から保護できます。
リフレクションについては、2 番目のモジュールのレッスンで詳しく学習します。そこでは、 Reflection API について説明します。 |
Java では列挙型のインスタンス化が禁止されています。この制限は、 ConstructorクラスのnewInstanceメソッドの実装に組み込まれており、リフレクションを通じてオブジェクトを作成するときによく呼び出されます。
Constructor.newInstanceからのコードの抜粋。 enumの作成に使用されます。
if ((clazz.getModifiers() & Modifier.ENUM) != 0)
throw new IllegalArgumentException("Cannot reflectively create enum objects");
列挙型 を使用してシングルトンを作成する場合の欠点は次のとおりです。
-
オブジェクトはすぐに作成され、初期化を遅らせることができないため、遅延初期化が行われません。
-
他のクラスの延長はできません。つまり、別のクラスを継承する必要がある場合、列挙型をシングルトンとして使用することはできません。このような場合は、静的メソッドまたはパブリック変数など、すでによく知られている他の実装オプションに頼る必要があります。
-
enum をシングルトンとして使用する場合、使用できるenumフィールドは 1 つだけです。
public enum Device extends Electricity {
PRINTER
}
このコードではコンパイル エラーが発生します。
ただし、インターフェイスを実装する必要がある場合、enum はインターフェイスを実装できるため、問題はありません。
public enum Device implements Electricity {
PRINTER
}
継承を使用する必要がない場合は、enumを介してシングルトン パターンを実装するのが最善です。これを推奨しているのは私たちだけではありません。Joshua Bloch 自身も同様に推奨しています。
この実装アプローチにより、利便性、コンパクトさ、すぐに使用できるシリアル化、リフレクション攻撃からの保護、および一意性など、優れたシングルトンに必要なものがすべて提供されます。