Java Language
シングルトン
サーチ…
前書き
列挙型シングルトン
public enum Singleton {
INSTANCE;
public void execute (String arg) {
// Perform operation here
}
}
列挙型はプライベートコンストラクタを持ち、最終的なものであり、適切なシリアル化機構を提供します。それらは非常に簡潔であり、スレッドセーフな方法で遅延的に初期化されます。
JVMは、列挙型の値がそれぞれ複数回インスタンス化されないことを保証します。これにより、列挙型のシングルトンパターンは反射攻撃に対して非常に強力な防御を提供します。
enumパターンが保護しないものは、物理的にソースコードに要素を追加している他の開発者です。そのため、シングルトンにこの実装スタイルを選択した場合、これらの列挙型に新しい値を追加しないことを明確に文書化することが不可欠です。
これは、Joshua BlochがEffective Javaで説明したように、シングルトンパターンを実装するための推奨される方法です。
ダブルチェックロック付きスレッドセーフシングルトン
このタイプのシングルトンはスレッドセーフであり、シングルトンインスタンスが作成された後の不要なロックを防ぎます。
public class MySingleton {
// instance of class
private static volatile MySingleton instance = null;
// Private constructor
private MySingleton() {
// Some code for constructing object
}
public static MySingleton getInstance() {
MySingleton result = instance;
//If the instance already exists, no locking is necessary
if(result == null) {
//The singleton instance doesn't exist, lock and check again
synchronized(MySingleton.class) {
result = instance;
if(result == null) {
instance = result = new MySingleton();
}
}
}
return result;
}
}
強調しなければならないのは、Java SE 5より前のバージョンでは、上記の実装が正しくないため、避けるべきです。 Java 5より前のJavaでは、ダブルチェックロックを正しく実装することはできません。
列挙型を使用しないシングルトン(熱心な初期化)
public class Singleton {
private static final Singleton INSTANCE = new Singleton();
private Singleton() {}
public static Singleton getInstance() {
return INSTANCE;
}
}
この例は事実上遅延初期化であると主張できます。 Java言語仕様の第12.4.1項は次のように述べています。
クラスまたはインタフェースタイプTは、次のいずれかが最初に現れる直前に初期化されます。
- Tはクラスであり、Tのインスタンスが作成される
- Tはクラスであり、Tによって宣言された静的メソッドが呼び出される
- Tで宣言された静的フィールドが割り当てられます。
- Tで宣言された静的フィールドが使用され、フィールドは定数変数ではありません
- Tは最上位クラスであり、T内に字句的にネストされたアサーション文が実行される。
したがって、クラスに他の静的フィールドや静的メソッドがない限り、初めてgetInstance()
メソッドが呼び出されるまで、 Singleton
インスタンスは初期化されません。
ホルダークラスを使用したスレッドセーフな遅延初期化| Bill Pughシングルトンの実装
public class Singleton {
private static class InstanceHolder {
static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return InstanceHolder.INSTANCE;
}
private Singleton() {}
}
これにより、 Singleton.getInstance()
への最初の呼び出しでINSTANCE
変数が初期化され、追加の同期化を必要とせずに静的初期化に対する言語のスレッド安全性が保証されます。
この実装はBill Pughシングルトンパターンとも呼ばれます。 [ウィキ]
シングルトンの拡張(シングルトン継承)
この例では、基本クラスのSingleton
は"Hello world!"
を返すgetMessage()
メソッドを提供しています"Hello world!"
メッセージ。
サブクラスUppercaseSingleton
とLowercaseSingleton
は、メッセージの適切な表現を提供するgetMessage()メソッドをオーバーライドします。
//Yeah, we'll need reflection to pull this off.
import java.lang.reflect.*;
/*
Enumeration that represents possible classes of singleton instance.
If unknown, we'll go with base class - Singleton.
*/
enum SingletonKind {
UNKNOWN,
LOWERCASE,
UPPERCASE
}
//Base class
class Singleton{
/*
Extended classes has to be private inner classes, to prevent extending them in
uncontrolled manner.
*/
private class UppercaseSingleton extends Singleton {
private UppercaseSingleton(){
super();
}
@Override
public String getMessage() {
return super.getMessage().toUpperCase();
}
}
//Another extended class.
private class LowercaseSingleton extends Singleton
{
private LowercaseSingleton(){
super();
}
@Override
public String getMessage() {
return super.getMessage().toLowerCase();
}
}
//Applying Singleton pattern
private static SingletonKind kind = SingletonKind.UNKNOWN;
private static Singleton instance;
/*
By using this method prior to getInstance() method, you effectively change the
type of singleton instance to be created.
*/
public static void setKind(SingletonKind kind) {
Singleton.kind = kind;
}
/*
If needed, getInstance() creates instance appropriate class, based on value of
singletonKind field.
*/
public static Singleton getInstance()
throws NoSuchMethodException,
IllegalAccessException,
InvocationTargetException,
InstantiationException {
if(instance==null){
synchronized (Singleton.class){
if(instance==null){
Singleton singleton = new Singleton();
switch (kind){
case UNKNOWN:
instance = singleton;
break;
case LOWERCASE:
/*
I can't use simple
instance = new LowercaseSingleton();
because java compiler won't allow me to use
constructor of inner class in static context,
so I use reflection API instead.
To be able to access inner class by reflection API,
I have to create instance of outer class first.
Therefore, in this implementation, Singleton cannot be
abstract class.
*/
//Get the constructor of inner class.
Constructor<LowercaseSingleton> lcConstructor =
LowercaseSingleton.class.getDeclaredConstructor(Singleton.class);
//The constructor is private, so I have to make it accessible.
lcConstructor.setAccessible(true);
// Use the constructor to create instance.
instance = lcConstructor.newInstance(singleton);
break;
case UPPERCASE:
//Same goes here, just with different type
Constructor<UppercaseSingleton> ucConstructor =
UppercaseSingleton.class.getDeclaredConstructor(Singleton.class);
ucConstructor.setAccessible(true);
instance = ucConstructor.newInstance(singleton);
}
}
}
}
return instance;
}
//Singletons state that is to be used by subclasses
protected String message;
//Private constructor prevents external instantiation.
private Singleton()
{
message = "Hello world!";
}
//Singleton's API. Implementation can be overwritten by subclasses.
public String getMessage() {
return message;
}
}
//Just a small test program
public class ExtendingSingletonExample {
public static void main(String args[]){
//just uncomment one of following lines to change singleton class
//Singleton.setKind(SingletonKind.UPPERCASE);
//Singleton.setKind(SingletonKind.LOWERCASE);
Singleton singleton = null;
try {
singleton = Singleton.getInstance();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
System.out.println(singleton.getMessage());
}
}