Javaでは、スーパーコンストラクタを実行する前にフィールドを初期化する方法はありますか?
私が思いつくことのできる最もいハッキングでさえ、コンパイラによって拒否されます。
class Base
{
Base(String someParameter)
{
System.out.println(this);
}
}
class Derived extends Base
{
private final int a;
Derived(String someParameter)
{
super(hack(someParameter, a = getValueFromDataBase()));
}
private static String hack(String returnValue, int ignored)
{
return returnValue;
}
public String toString()
{
return "a has value " + a;
}
}
注:継承から委任に切り替えたときに問題はなくなりましたが、まだ知りたいです。
いずれの場合でもスーパーコンストラクターは実行されますが、「最もひどいハッキング」について話しているため、これを利用できます。
public class Base {
public Base() {
init();
}
public Base(String s) {
}
public void init() {
//this is the ugly part that will be overriden
}
}
class Derived extends Base{
@Override
public void init(){
a = getValueFromDataBase();
}
}
この種のハックの使用はお勧めしません。
これを行う方法がありました。
class Derived extends Base
{
private final int a;
// make this method private
private Derived(String someParameter,
int tmpVar /*add an addtional parameter*/) {
// use it as a temprorary variable
super(hack(someParameter, tmpVar = getValueFromDataBase()));
// assign it to field a
a = tmpVar;
}
// show user a clean constructor
Derived(String someParameter)
{
this(someParameter, 0)
}
...
}
他の人が言ったように、スーパークラスコンストラクターを呼び出す前にインスタンスフィールドを初期化することはできません。
しかし、回避策があります。 1つは、値を取得してDerivedクラスのコンストラクターに渡すファクトリクラスを作成することです。
class DerivedFactory {
Derived makeDerived( String someParameter ) {
int a = getValueFromDataBase();
return new Derived( someParameter, a );
}
}
class Derived extends Base
{
private final int a;
Derived(String someParameter, int a0 ) {
super(hack(someParameter, a0));
a = a0;
}
...
}
Java言語仕様(セクション8.8.7) で禁止されています:
コンストラクター本体の最初のステートメントは、同じクラスまたは直接のスーパークラスの別のコンストラクターの明示的な呼び出しである場合があります。
コンストラクターの本体は次のようになります。
ConstructorBody:
{ ExplicitConstructorInvocationopt BlockStatementsopt }