web-dev-qa-db-ja.com

スーパーコンストラクターを実行する前にフィールドを初期化しますか?

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;
    }
}

注:継承から委任に切り替えたときに問題はなくなりましたが、まだ知りたいです。

35
fredoverflow

いずれの場合でもスーパーコンストラクターは実行されますが、「最もひどいハッキング」について話しているため、これを利用できます。

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();
    }
} 

この種のハックの使用はお勧めしません。

11

これを行う方法がありました。

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)
    }

    ...
}
8
luobo25

他の人が言ったように、スーパークラスコンストラクターを呼び出す前にインスタンスフィールドを初期化することはできません。

しかし、回避策があります。 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;
    }
    ...
}
4
Andy Thomas

Java言語仕様(セクション8.8.7) で禁止されています:

コンストラクター本体の最初のステートメントは、同じクラスまたは直接のスーパークラスの別のコンストラクターの明示的な呼び出しである場合があります。

コンストラクターの本体は次のようになります。

ConstructorBody:

{ ExplicitConstructorInvocationopt BlockStatementsopt }
1
dcernahoschi