Как создать “абстрактное поле”?

Я знаю, что абстрактные поля не существуют в Java. Я также считал этот вопрос, но предложенные решения не решат мою проблему. Возможно, нет никакого решения, но это стоит спросить :)

Проблема

У меня есть абстрактный класс, который делает операцию в конструкторе в зависимости от значения одного из его полей. Проблема состоит в том, что значение этого поля изменится в зависимости от подкласса. Как я могу сделать так, чтобы операция была сделана на значении поля, переопределенного подклассом?

Если я просто "переопределяю" поле в подклассе, операция сделана на значении поля в абстрактном классе.

Я открыт для любого решения, которое гарантировало бы, что операция будет сделана во время инстанцирования подкласса (т.е. помещение операции в методе, названном каждым подклассом в конструкторе, не является допустимым решением, потому что кто-то мог бы расширить абстрактный класс и забыть называть метод).

Кроме того, я не хочу давать значение поля как аргумент конструктора.

Там какое-либо решение состоит в том, чтобы сделать это, или я должен просто изменить свой дизайн?


Править:

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

tools[0]=new Hand(this);
tools[1]=new Pencil(this);
tools[2]=new AddObject(this);

(подклассы являются Рукой, Карандашом и AddObject, что все расширяют Инструмент абстрактного класса),

Вот почему я не хочу изменять конструктора.

Решение, которое я собираюсь использовать, состоит в том, чтобы немного изменить вышеупомянутый код на:

tools[0]=new Hand(this);
tools[0].init();
tools[1]=new Pencil(this);
tools[1].init();
tools[2]=new AddObject(this);
tools[2].init();

и используйте абстрактный метод считывания для получения доступ к полю.

8
задан Community 23 May 2017 в 12:02
поделиться

7 ответов

Как насчет абстрактного геттера / сеттера для поля?

abstract class AbstractSuper {
    public AbstractSuper() {
        if (getFldName().equals("abc")) {
            //....
        }
    }

    abstract public void setFldName();
    abstract public String getFldName();
}

class Sub extends AbstractSuper {
    @Override
    public void setFldName() {
        ///....
    }

    @Override
    public String getFldName() {
        return "def";
    }
}
13
ответ дан 5 December 2019 в 05:25
поделиться

Я думаю, вам нужна фабрика (также известная как «виртуальный конструктор»), которая может воздействовать на этот параметр.

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

0
ответ дан 5 December 2019 в 05:25
поделиться

Кроме того, я не хочу приводить значение поля как аргумент конструктора .

Почему нет? Это идеальное решение. Сделайте конструктор защищенным и не предлагайте конструктора по умолчанию, а разработчики подкласса вынуждены указывать значение в своих конструкторах, которое может быть общедоступным и передавать постоянное значение суперклассу, делая параметр невидимым для пользователей подклассы.

public abstract class Tool{
    protected int id;
    protected Main main;
    protected Tool(int id, Main main)
    {
        this.id = id;
        this.main = main;
    }
}

public class Pencil{
    public static final int PENCIL_ID = 2;
    public Pencil(Main main)
    {
        super(PENCIL_ID, main);
    }
}
13
ответ дан 5 December 2019 в 05:25
поделиться

Кроме того, я не хочу указывать значение поля в качестве аргумента конструктора.

Есть ли какое-то решение для этого, или я должен просто изменить свой дизайн?

Да, я думаю, вам следует изменить свой дизайн, чтобы подкласс передавал значение конструктору. Поскольку подклассовая часть вашего объекта не инициализируется до после возврата конструктора суперкласса, на самом деле нет другого чистого способа сделать это. Конечно, это сработает:

class Super {
    protected abstract int abstractField();
    protected Super() { System.out.println("Abstract field: " + abstractField); }
}
class Sub { 
    protected int abstractField(){ return 1337; }
}

... поскольку реализация abstractField () не работает с состоянием объекта. Однако вы не можете гарантировать, что подклассы не сочтут отличной идеей быть немного более динамичным, и пусть abstractField () вернет непостоянное значение:

class Sub2 {
    private int value = 5; 
    protected int abstractField(){ return value; }
    public void setValue(int v){ value = v; }
}
class Sub3 {
    private final int value; 
    public Sub3(int v){ value = v; }
    protected int abstractField(){ return value; }
}

Это не то, что вы ожидаете этого, поскольку инициализаторы и конструкторы подклассов запускаются после таковых из суперкласса. Оба new Sub2 () и new Sub3 (42) будут печатать Абстрактное поле: 0 , поскольку поля значение не были инициализированы когда вызывается abstractField () .

Передача значения конструктору также имеет дополнительное преимущество, заключающееся в том, что поле, в котором вы храните значение, может быть final .

1
ответ дан 5 December 2019 в 05:25
поделиться

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

1
ответ дан 5 December 2019 в 05:25
поделиться

Вы не можете сделать это в конструкторе, так как суперкласс будет инициализирован раньше, чем что-либо в подклассе. Таким образом, доступ к значениям, специфичным для вашего подкласса, в вашем суперконструкторе не удастся.
Рассмотрите возможность использования фабричного метода для создания вашего объекта. Например:

private MyClass() { super() }
private void init() { 
    // do something with the field
}
public static MyClass create() {
    MyClass result = new MyClass();
    result.init();
    return result;
}

У вас есть проблема в этом конкретном примере, когда MyClass нельзя разделить на подклассы, но вы можете сделать конструктор защищенным. Убедитесь, что в вашем базовом классе есть общедоступный / защищенный конструктор для этого кода. Это просто показано, что вам, вероятно, потребуется двухэтапная инициализация для того, что вы хотите сделать.

Еще одно возможное решение, которое вы могли бы использовать, - это использование класса Factory, который создает все варианты этого абстрактного класса, и вы можете передать поле в конструктор. Ваша Factory будет единственной, кто знает о поле, и пользователи Factory могут не обращать на это внимания.

РЕДАКТИРОВАТЬ: Даже без фабрики вы могли бы заставить свой абстрактный базовый класс требовать в конструкторе, поэтому все подклассы должны передавать ему значение при создании экземпляра.

1
ответ дан 5 December 2019 в 05:25
поделиться

Как насчет использования шаблона шаблона?

public abstract class Template {

    private String field;

    public void Template() {
        field = init();
    }

    abstract String init();
}

Таким образом, вы заставляете все подклассы реализовать метод init () , который, поскольку он вызывается конструктором, назначит вам поле.

2
ответ дан 5 December 2019 в 05:25
поделиться
Другие вопросы по тегам:

Похожие вопросы: