次のように、べき等で常に同じ値を返し、チェック例外をスローする可能性のある引数のない静的メソッドがあるとします。
class Foo {
public static Pi bar() throws Baz { getPi(); } // gets Pi, may throw
}
返されたオブジェクトを構築するものが高価で決して変更されない場合、これは遅延シングルトンの良い候補です。1 つの選択肢は、Holder パターンです。
class Foo {
static class PiHolder {
static final Pi PI_SINGLETON = getPi();
}
public static Pi bar() { return PiHolder.PI_SINGLETON; }
}
残念ながら、(暗黙の) 静的初期化ブロックからはチェック済み例外をスローできないため、これはうまくいきません。コールbar()):
class Foo {
static class PiHolder {
static final Pi PI_SINGLETON;
static {
try {
PI_SINGLETON = = getPi(); }
} catch (Baz b) {
throw new ExceptionInInitializerError(b);
}
}
public static Pi bar() throws Bar {
try {
return PiHolder.PI_SINGLETON;
} catch (ExceptionInInitializerError e) {
if (e.getCause() instanceof Bar)
throw (Bar)e.getCause();
throw e;
}
}
この時点で、ロックを再確認したほうがすっきりするのではないでしょうか?
class Foo {
static volatile Pi PI_INSTANCE;
public static Pi bar() throws Bar {
Pi p = PI_INSTANCE;
if (p == null) {
synchronized (this) {
if ((p = PI_INSTANCE) == null)
return PI_INSTANCE = getPi();
}
}
return p;
}
}
DCL はまだアンチパターンですか? ここで見逃している他の解決策はありますか (際どいシングル チェックのようなマイナー バリアントも可能ですが、解決策を根本的に変更しないでください)。どちらかを選択する正当な理由はありますか?
上記の例は試していないので、コンパイルできない可能性は十分にあります。
編集: このシングルトンの消費者 (つまり、の呼び出し元) を再実装または再構築する余裕はありFoo.bar()ません。また、この問題を解決するために DI フレームワークを導入する機会もありません。私は主に、特定の制約内で問題を解決する回答 (呼び出し元に伝達されるチェック済み例外を含むシングルトンの提供) に興味があります。
更新:結局、DCL を使用することにしました。これは、既存のコントラクトを保持する最もクリーンな方法を提供し、適切に行われた DCL を避けるべき具体的な理由を誰も提供しなかったためです。同じことを達成するための非常に複雑な方法であるように思われたため、受け入れられた回答ではこの方法を使用しませんでした。