インスタンス初期化子の存在意義が理解できました

Javaの機能として用意されているインスタンス初期化子とクラス初期化子について、クラス初期化子は用途がなんとなくわかった(他に代替がない機能)のですが、インスタンス初期化子はコンストラクタで良くないのかなあ、と思っていたのですが、無名クラスの定義の際に必要だということに気づきました。

確かに要るなぁと感心したのでメモしときます。

クラス初期化子とインスタンス初期化子

クラス初期化子とインスタンス初期化子の記述方法と挙動は以下の簡単なプログラムで理解できます。

クラス初期化子は、ClassInitializer.a();だけではなく、ClassInitializer.n = 5;などとした場合も一番最初に呼び出されます。こういう動作は、代替となる記述方法がない(少なくても私は知らない)ので、必要なのかなと感じました。

一方、インスタンス初期化子は、コンストラクタで十分代用可能なのではと感じていました。

class InitTest{
	
	public static void main(String[] args){
		ClassInitializer.a();
		new InstanceInitializer();
	}
}

// クラス初期化子
class ClassInitializer{
	static {
		System.out.println("call: ClassInitializer");
	}
	public static int n;
	public static void a(){
		System.out.println("call: ClassInitializer.a()");
	}
}

// インスタンス初期化子
class InstanceInitializer{
	{
		System.out.println("call: InstanceInitializer");
	}
	InstanceInitializer(){
		System.out.println("call: InstanceInitializer()");
	}
}

/*
$ java InitTest
call: ClassInitializer
call: ClassInitializer.a()
call: InstanceInitializer
call: InstanceInitializer()
*/

必要だと感じる場面

無名クラスは読んで字の如く「名前を持たないクラス」なので、コンストラクタの要件である「クラスと同じ名前を持たせる」をどう頑張っても満たすことができません。

class InitTest2{
	public static void main(String[] args){
		// InstanceInitializerInterfaceをimplementsしたクラスをnewする記述
		new InstanceInitializerInterface(){
			// クラス名を持たないためコンストラクタの要件(クラスと同じ名前を持たせる)
			// を満たせない(=無名クラスであるためコンストラクタが定義できない)
			// InstanceInitializerInterface(){
			// 	System.out.println("call: InstanceInitializerInterface()");
			// }
			{
				// インスタンス初期化子を使うしかない
				System.out.println("call: ClassInitializer");
			}
			public void a(){
				System.out.println("call: InstanceInitializerInterface.a()");
			}
		};
	}
}
interface InstanceInitializerInterface{
	public void a();
}
/*
$ java InitTest2
call: ClassInitializer
*/

番外編

コンストラクタとインスタンス初期化子はどっちが先に実行されるんだろうと思ってテストコード書いてみると、こんな実行結果になりました。

日本語にするのが難しいですが、ひとつ思ったことがありますので、つらつらと書いておきます。初期化子という名前から、そのクラスの中で真にやらなければならないメンバ等の初期化を実施するための機構だと感じました。何が言いたいかというと、コンストラクタは機能を満足するための準備をする機構だと考えます。そのクラスに求められていることの数だけコンストラクタが用意されていますが、根底としての初期化はどのコンストラクタでも共通のはずで、そういった意味での真の初期化を行う場所なのかなと。

でもまあ、普通はコンストラクタに書きたい気はしますけどね。どういう実装がスタンダードなのかわかりませんが。

class InitTest2{
	public static void main(String[] args){
		new InstanceInitializerClass(){
			{
				System.out.println("call: InstanceInitializer");
			}
		};
	}
}
class InstanceInitializerClass{
	{
		System.out.println("call: super InstanceInitializer");
	}
	
	InstanceInitializerClass(){
		System.out.println("call: InstanceInitializerClass()");
	}
}
/*
$ java InitTest2
call: super InstanceInitializer
call: InstanceInitializerClass()
call: InstanceInitializer
*/

コメントを残す

メールアドレスが公開されることはありません。