Как создать «абстрактное поле»? - PullRequest
8 голосов
/ 10 апреля 2010

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

Проблема

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

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

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

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

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


Edit:

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

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

(подклассами являются Hand, Pencil и AddObject, которые расширяют абстрактный класс Tool)

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

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

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 ]

14 голосов
/ 10 апреля 2010

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

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

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 голосов
/ 10 апреля 2010

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

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";
    }
}
2 голосов
/ 10 апреля 2010

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

public abstract class Template {

    private String field;

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

    abstract String init();
}

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

1 голос
/ 10 апреля 2010

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

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

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

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) будут печатать Abstract field: 0, поскольку поля value не были инициализированы при вызове abstractField().

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

1 голос
/ 10 апреля 2010

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

1 голос
/ 10 апреля 2010

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

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, который создает все варианты этого абстрактного класса, и вы можете передать поле в конструктор. Ваша Фабрика будет единственной, кто знает о поле, и пользователи Фабрики могут не обращать на это внимания.

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

0 голосов
/ 24 февраля 2014

Если я вас правильно понимаю: вы хотите, чтобы конструктор абстрактного класса делал что-то в зависимости от поля в абстрактном классе, но которое задается (надеюсь) подклассом?

Если я ошибся, вы можетепрекрати читать ...

Но если я правильно понял, ты пытаешься сделать что-то невозможное.Поля класса создаются в лексическом порядке (и поэтому, если вы объявите поля «ниже» или «после» конструктора, то они не будут созданы до вызова конструктора).Кроме того, JVM проходит через весь суперкласс, прежде чем делать что-либо с подклассом (именно поэтому вызов super () в конструкторе подкласса должен быть первой инструкцией в конструкторе ... потому что это всего лишь "совет"JVM о том, как запустить конструктор суперкласса).

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

И этопочему у вас не может быть абстрактных полей: абстрактное поле не существовало бы в абстрактном классе (но только в подклассе) и поэтому серьезно (!) «запрещено» супер (абстрактному) классу ... потому что JVMне может ничего связать ссылки на поле (потому что оно не существует).

Надеюсь, это поможет.

0 голосов
/ 10 апреля 2010

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...