Java передает данные экземпляра подкласса конструкторам суперкласса - PullRequest
1 голос
/ 13 декабря 2011

Кто-нибудь знает, есть ли способ в 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
    }
}

Ответы [ 6 ]

1 голос
/ 13 декабря 2011

Во-первых, пожалуйста, не надо.

Во-вторых, действительно, это действительно плохая идея. Не. Подумайте о том, что вы пытаетесь сделать в более широком контексте.

Если вам абсолютно необходимо это сделать, вы можете спрятать его в ThreadLocal. Вы можете вызвать (неэкземплярный) метод, оценивая выражение, результат которого передается в super() или this() (возможно, это единственная причина, по которой вам нужен второй частный конструктор, который, возможно, принимает Void ( «V» (аргумент). Это так плохо, я даже не собираюсь записывать код.

В отредактированном примере кода просто передайте наборы в защищенный конструктор. Если у вас много аргументов, возможно, некоторые подклассы являются особыми для некоторых аргументов, вы можете заключить все аргументы в один объект аргумента.

Существует еще один действительно хакерский подход, если у вас есть -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);
            }
        };
    }
}
0 голосов
/ 13 декабря 2011

Мне нужно установить переменные экземпляра, определенные в суперклассе, по-разному, в зависимости от типа подкласса, но я все еще хочу иметь возможность совместно использовать общий код не-конструктора между различными экземплярами подкласса.

В этом случае создайте защищенный конструктор в суперклассе и передайте ему все настроенные значения при создании подкласса.

0 голосов
/ 13 декабря 2011

Как и другие говорили, не делай этого.Если вы хотите разделить некоторый код между этими классами, попробуйте вместо наследования использовать инкапсуляцию / инкапсуляцию.

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();
    }
}
0 голосов
/ 13 декабря 2011

Вы никогда не должны вызывать какой-либо "чужой" метод (т. Е. Переопределяемый метод этого класса или любой метод из любого другого класса) внутри конструктора.Пока объект не полностью инициализирован, у вас могут быть побочные эффекты, подобные тому, который вы видите.

В вашем случае, в конструкторе подкласса super () вызывается даже до того, как установлено значение4. Это означает, что конструктор суперкласса вызывается, а затем вызывает метод «setup», в то время как «value» все еще находится в 0.

Только после возврата конструктора суперкласса «value» устанавливается в4. И тогда уже слишком поздно.

Я бы порекомендовал установить для переменной "o1" значение protected, чтобы подклассы могли сами устанавливать ее значение.

0 голосов
/ 13 декабря 2011

В Java, если вы хотите вызвать конструктор базового класса, вы должны сделать это в первой строке конструктора вашего подкласса.Таким образом, ответ - нет, вы не можете установить this.value перед вызовом конструктора суперкласса.

Но метод setup() вашего подкласса уже вызывается в конструкторе суперкласса.Почему бы вам не установить там свое значение?

ОБНОВЛЕНИЕ: Извините, я не обратил внимание, что ваш метод 'setup ()' возвращает значение.Что вы можете сделать, это сделать абстрактный init() метод в вашем суперклассе и вызвать его в вашем суперструкторе, прежде чем вызывать метод setup().Таким образом, подклассы будут вынуждены реализовать init(), и вы будете знать, что это место для инициализации членов любого подкласса до того, как они будут использованы в вашем суперклассе.

При этомэтот подход не навязывает вам безопасность.Когда вы вызываете супер-конструктор из вашего под-конструктора, экземпляр подкласса только начинает создаваться.Ему все еще нужно выполнить остальную часть кода в суб-конструкторе, прежде чем объект будет безопасно создан.

И в этой ситуации супер-конструктор запускает и вызывает метод init() на вашем just-in-подкласс процесса создания.Это означает, что если вы идете с подходом, вы должны быть особенно осторожны в том, что вы делаете в классе init().

0 голосов
/ 13 декабря 2011

Поместите общий код, который вы хотите запустить, в защищенный метод, а не в конструктор. Вызывайте этот метод, когда хотите.

...