1

スーパークラスのコンストラクターを呼び出す前に、Java でサブクラスのインスタンス変数の値を設定する方法があるかどうかは誰にもわかりません。私が達成しようとしていることの簡単な概略図を以下に示します。サブクラスのタイプに応じて、スーパークラスで定義されたインスタンス変数を別々に設定する必要がありますが、共通の非コンストラクター コードを共有できるようにしたいと考えています。サブクラスの異なるインスタンス。

これを行うためのきれいな方法はありますか?おそらく、私が見逃しているコーディングパターンのようなものですか? アイデアをお寄せいただきありがとうございます。

public abstract class ConstraintSatisfactionProblem {

    final Set<Variable> variables;
    final Set<Constraint> constraints;

    public Foo() {
        this.variables = setupVariables();
        this.constraints = setupConstraints();
    }

    public abstract Set<Variable> setupVariables();

    public abstract Set<Constraint> setupConstraints();

    public Map<Variable, Constraint> solve() { ... }
}

public class WordSquare extends ConstraintSatisfactionProblem {

    final int size;
    final static Set<Character> domain = ...;

    public WordSquare() {
        super();         // can I simulate calling super() after setting this.value = 4?
        this.value = 4;
    }

    public Set<Variable> setupVariables() {
        this.variables = new HashSet<Variable>();
        for(int row = 0; row < size; ++row) {
            for(int col = 0; col < size; ++col) {
               variables.add(new Variable<Pair, Character>(new Pair(row, col), domain);
            }
        }
        return this.variables;
    }

    public Set<Constraint> setupConstraints() {
        // setup code specific to this problem
    }
}

public class Cryptarithmetic extends ConstraintSatisfactionProblem {

    final String problem;

    public Cryptarithmetic(String problem) {
        super();
        this.problem = problem;
    }

    public Set<Variable> setupVariables() {
        this.variables = new HashSet<Variable>();
        for(char c : problem.toCharArray()) {
            variables.add(new Variable<Character, Integer>(c, getDomain());
            }
        } 
        return this.variables;
    }

    public Set<Constraint> setupConstraints() {
        // setup code specific to this problem
    }
}
4

6 に答える 6

1

まず、しないでください。

第二に、それは本当に悪い考えです。しないでください。より広い文脈で何をしようとしているのかを考えてください。

どうしてもしなければならない場合は、ThreadLocal. super()結果がorに渡される式を評価することにより、(インスタンスではない) メソッドを呼び出すことができます(おそらく、 (大文字の 'V') 引数this()を取る可能性のある 2 番目のプライベート コンストラクターが必要な唯一の理由です)。Voidそれはとても邪悪なので、コードを書き留めるつもりもありません。

編集したサンプル コードでは、セットを保護されたコンストラクターに渡すだけです。多くの引数があり、一部のサブクラスが一部の引数について特別である可能性がある場合、すべての引数を単一の引数オブ​​ジェクトにラップすることができます。

あなたが持っているか後である限り、別の本当にハッキーなアプローチが-target 1.4あります(あなたがすべきです!)。サブクラスを内部クラス (おそらく匿名) にします。外側の this およびその他のキャプチャされた変数への参照は、スーパー コンストラクターを呼び出す前に使用できます。

public class Outer {
    // What a hack!
    private static abstract class Base {
        Base() {
            hello(); // Calling a virtual method in a constructor - poor form.
        }
        abstract void hello();
    }
    public static void main(String[] args) {
        // Do not do this.
        final String hi = "Hi!";
        new Base() {
            void hello() {
                // Really, don't do it.
                System.err.println(hi);
            }
        };
    }
}
于 2011-12-13T17:32:48.973 に答える
0

サブクラスのタイプに応じて、スーパークラスで定義されたインスタンス変数を別々に設定する必要がありますが、サブクラスの異なるインスタンス間で共通の非コンストラクター コードを共有できるようにしたいと考えています。

その場合、スーパークラスで保護されたコンストラクターを作成し、サブクラスを構築するときにカスタマイズされたすべての値をそれに渡します。

于 2011-12-13T18:54:50.787 に答える
0

他の人が言ったように、これをしないでください。これらのクラス間でコードを共有したい場合は、継承の代わりに包含/カプセル化を試してください。

public class Foo {

    private final Object o1;

    public Foo(Object o) {
        o1 = o;
    }

    public void complexMethodCommonToAllSubclassesOfFoo() { ... }
    }

public class Bar {

    private final int value;
    private final Foo foo;

    public Bar() {
        super(); 
        this.value = 4;
        this.foo = new Foo( new Object() ); // or whatever
    }

    // If you need to expose complexMethodCommonToAllSubclassesOfFoo to clients of this class, just add the method and delegate to foo like this
    public void complexMethodCommonToAllSubclassesOfFoo() {
        foo.complexMethodCommonToAllSubclassesOfFoo();
    }
}
于 2011-12-13T18:06:38.340 に答える
0

コンストラクターではなく、保護されたメソッドで実行する共通コードを配置します。必要に応じてそのメソッドを呼び出します。

于 2011-12-13T17:33:48.747 に答える
0

Java では、基本クラスのコンストラクターを呼び出したい場合、サブクラスのコンストラクターの最初の行でそれを行う必要があります。this.valueスーパークラスのコンストラクターを呼び出す前に設定することはできません。

ただし、サブクラスのsetup()メソッドは、スーパーのコンストラクターで既に呼び出されています。そこに値を設定してみませんか?

更新: 申し訳ありませんが、「setup()」メソッドが値を返すことに注意を払っていませんでした。あなたができることはinit()、スーパークラスで抽象メソッドを作成し、メソッドを呼び出す前にスーパーコンストラクターで呼び出すことですsetup()。このようにして、サブクラスは強制的に実装init()され、スーパークラスで使用される前にサブクラスのメンバーを初期化する場所であることがわかります。

そうは言っても、このアプローチはあなたに安全を強制するものではありません. サブコンストラクターからスーパー コンストラクターを呼び出すと、サブクラスのインスタンスが作成され始めます。オブジェクトが安全に作成される前に、サブコンストラクターで残りのコードを実行する必要があります。

この状況では、スーパー コンストラクターが移動し、init()作成中のサブクラスのメソッドを呼び出します。init()これは、アプローチを使用する場合、クラスで何をするかについて特に注意する必要があることを意味します.

于 2011-12-13T17:35:21.607 に答える
0

コンストラクター内で「エイリアン」メソッド (つまり、このクラスのオーバーライド可能なメソッド、または他のクラスのメソッド) フォームを呼び出してはなりません。オブジェクトが完全に初期化されていない限り、このような副作用が発生する可能性があります。

あなたの場合、サブクラスのコンストラクターでは、「値」が 4 に設定される前でも super() が呼び出されます。つまり、スーパークラスのコンストラクターが呼び出され、「値」がまだ0.

スーパークラス コンストラクターが返されたときにのみ、「値」が 4 に設定されます。それでは手遅れです。

私がお勧めするのは、「o1」変数を保護に設定して、サブクラスがその値を自分で設定できるようにすることです。

于 2011-12-13T17:36:25.913 に答える