読者です 読者をやめる 読者になる 読者になる

Fight the Future

何かを始めたら、半分成功したのと同じ

君は合成コンストラクタを知っているか!(Java)

シリーズ第3弾!
君は合成メソッドを知っているか!(Java) - Fight the Future じゅくのblog
君は合成フィールドを知っているか!(Java) - Fight the Future じゅくのblog


合成コンストラクタ。つまりコンパイラが自動的に作成するコンストラクタ。
やはりインナークラス関連で作成される。


アウタークラスのコンストラクタがprivateとする。
でもインナークラスであれば、そのprivateなアウタークラスのコンストラクタを呼び出すことができる。
こういう場合、合成コンストラクタが作成される。

このこともSunのサイトに記述があった。
Retrieving and Parsing Constructor Modifiers (The Java™ Tutorials >
The Reflection API > Members)


少しコードを簡略化して動作させてみた。

import java.lang.reflect.Constructor;

public class SyntheticConstructor {
	private SyntheticConstructor() {}

	class Inner {
		Inner() {
			// Compiler will generate a synthetic constructor since
			// SyntheticConstructor() is private.
			new SyntheticConstructor();
		}
	}

	public static void main(String... args) {
		try {
			Class<?> c = Class.forName("SyntheticConstructor");
			Constructor[] allConstructors = c.getDeclaredConstructors();
			for (Constructor ctor : allConstructors) {
				System.out.format("%s%n", ctor.toGenericString());
				System.out.format("  [ synthetic=%-5b ]%n", ctor.isSynthetic());
			}
		} catch (ClassNotFoundException x) {
			x.printStackTrace();
		}

	}
}

アウタークラスがSyntheticConstructorクラス、インナークラスがInnerクラス。
Innerクラスのコンストラクタでアウタークラスのprivateコンストラクタを呼び出している。
このコードを実行するとこうなる。

private SyntheticConstructor()
  [ synthetic=false ]
 SyntheticConstructor(SyntheticConstructor)
  [ synthetic=true  ]

SyntheticConstructorクラスに、もともとあるprivateなコンストラクタの他に、修飾子なしの(package privateの)コンストラクタが作成されている。
これが合成コンストラクタ。


こう記述がある。

Since the inner class's constructor references the private constructor of the enclosing class, the compiler must generate a package-private constructor. The parameter type SyntheticConstructor$1 is arbitrary and dependent on the compiler implementation. Code which depends on the presence of any synthetic or non-public class members may not be portable.

Retrieving and Parsing Constructor Modifiers (The Java™ Tutorials > The Reflection API > Members)

エンクロージングクラスのprivateコンストラクタを参照するインナークラスのコンストラクタがあるので、コンパイラはpackage-privateなコンストラクタを生成する。
引数の型SyntheticConstructor$1はコンパイラの実装に依存する。
こういうのをコードで使うとポータブルじゃないよ。


ところで、Sunのサイトの実行結果は「SyntheticConstructor(SyntheticConstructor$1)」とあるけど、
僕の実行結果は「SyntheticConstructor(SyntheticConstructor)」だった。
Macだからコンパイラの実装も異なってるからかな。

よかったらいろんなOSの人、実行してみてもらえません??
コピペで動きます。
結果をコメントでもらえるとうれしいです。