SingletonとExceptionについての自分の結論

SingletonとExceptionについて考えてたら、なんだか良く分からなくなってしまいました。
で、SingletonパターンとExceptionについて考えてみたのですが、良くわからず・・・。
結論として↓の実装を考えてみたものの、問題ない実装であることの証拠も見つけられず・・・。
手詰まりのまま記事を公開していたのですが、その後もう少し調べたところ↓の実装で問題ないことが分かりました。

public class Singleton {

	private static class Inner {
		private static final Singleton singleton = new Singleton();;
	}

	public static Singleton getInstance() {
		try {
			return Inner.singleton;
		} catch (ExceptionInInitializerError e) {
			Throwable original = e.getCause();
			// 何かエラー処理
			throw new ProjectOriginalException("catched by Singleton!", original);
		}
	}

	private Singleton() {
		throw new RuntimeException("Singleton constructor failure!");
	}
}

根拠は下記Java仕様書の「12.4 Initialization of Classes and Interfaces」。
The Java Language Specification Third Edition
重要なところを訳してみると、

Classが初期化されるのは、class or interfaceのTが

  1. Tがclassで、インスタンス化されるとき
  2. Tがclassで、staticメソッドが起動されたとき
  3. Tに定義されたstaticフィールドが割り当てられたとき
  4. Tに定義されたstaticフィールドが使用される かつ そのstaticフィールドがコンスト値*1ではないとき
  5. Tがトップレベルクラス*2で、Tに含まれたassert文が実行されたとき。

上記操作をリフレクションで行ってもClassは初期化される。
上記以外の状況ではclass or interfaceは初期化されない。

と書かれておりました。(※5番の訳は自信ないです。)
よって、今回の↑のSingleton実装ではインナークラスのInnerが初期化されるタイミングは、getInstanceメソッド内部で「Inner.singleton」が呼び出されたタイミングだけ、言い換えると上記4番の条件を満たしたときだけです。
それ以外の状況では初期化されないので、Singletonインスタンス化のときに発生した例外は、必ずgetInstanceメソッド内のtry-catchで処理できることになります。

謝辞

今回の結論を出すきっかけを作ってくれたのは、英語版Wikipediaの下記記事でした。
Sigleton Pattern
自分の書いたSingletonが、真ん中辺りの「The solution of Bill Pugh」のサンプルソースにそっくりなことに気がついて、そこから調査を始めました。
Wikipediaの記事には

The technique known as the initialization on demand holder idiom, is as lazy as possible, and works in all known versions of Java.

とも書いてあったので、「The solution of Bill Pugh」が全バージョンのJava環境で動作することまでは分かったのですが、これは遅延初期化を意図したテクニックなので、Exception処理を意図した↑でも同じ話が通用するのかが分かりませんでした。

何か情報はないかと思って更にググってみたところ、
じゅんいち☆かとうの技術日誌-シングルトンパターンの遅延初期化をスレッドセーフにするには
にたどり着きました。
分かりやすく詳細に書かれていて、とても参考になりました。
ありがとうございます!
上記記事にはJava仕様書のどこが根拠となっているかの記述まで書いてあったので、自分で仕様書をチェックすることができました。
いや、本当に助かりました。

おまけ

上記Java仕様書の翻訳を改めて見てみて気づいたんですが・・・フィールドを持たないinterfaceって初期化されなくないですか?
まぁ確かに、フィールドのないinterfaceは初期化処理なくても成り立ちそうな気はするのですが・・・本当にそうなのかな?
誰か、知ってる人いませんかね・・・?

*1:プリミティブ値 or String型でfinal宣言された、コンパイル時に解決される値

*2:インナークラスじゃないクラス