如何创建“抽象字段”?

发布于 2024-08-28 17:57:23 字数 960 浏览 6 评论 0原文

我知道java中不存在抽象字段。我还阅读了这个问题,但提出的解决方案无法解决我的问题。也许没有解决方案,但值得一问:)

问题

我有一个抽象类,它根据其字段之一的值在构造函数中执行操作。 问题是该字段的值会根据子类而变化。 如何才能对子类重新定义的字段的值进行操作?

如果我只是“覆盖”子类中的字段,则操作是对抽象类中字段的值完成的。

我对任何能够确保在子类实例化期间完成操作的解决方案持开放态度(即,将操作放在构造函数中每个子类调用的方法中不是有效的解决方案,因为有人可能会扩展抽象类并忘记调用该方法)。

另外,我不想将字段的值作为构造函数的参数给出。

有什么解决方案可以做到这一点,还是我应该改变我的设计?


编辑:

我的子类实际上是我的主程序使用的一些工具,因此构造函数必须是公共的,并准确地采用调用它们的参数:(

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

并使用抽象 getter 来访问该字段。

I know abstract fields do not exist in java. I also read this question but the solutions proposed won't solve my problem. Maybe there is no solution, but it's worth asking :)

Problem

I have an abstract class that does an operation in the constructor depending on the value of one of its fields.
The problem is that the value of this field will change depending on the subclass.
How can I do so that the operation is done on the value of the field redefined by the subclass ?

If I just "override" the field in the subclass the operation is done on the value of the field in the abstract class.

I'm open to any solution that would ensure that the operation will be done during the instantiation of the subclass (ie putting the operation in a method called by each subclass in the constructor is not a valid solution, because someone might extend the abstract class and forget to call the method).

Also, I don't want to give the value of the field as an argument of the constructor.

Is there any solution to do that, or should I just change my design ?


Edit:

My subclasses are actually some tools used by my main program, so the constructor has to be public and take exactly the arguments with which they will be called:

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

(the subclasses are Hand, Pencil and AddObject that all extend the abstract class Tool)

That's why I don't want to change the constructor.

The solution I'm about to use is to slightly change the above code to:

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

and use an abstract getter to acces the field.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

々眼睛长脚气 2024-09-04 17:57:23

字段的抽象 getter/setter 怎么样?

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

How about abstract getter/setter for field?

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";
    }
}
地狱即天堂 2024-09-04 17:57:23

另外,我不想给出这个值
该字段作为参数
构造函数。

为什么不呢?这是完美的解决方案。使构造函数受保护并且不提供默认构造函数,并且子类实现者被迫在其构造函数中提供一个值 - 该值可以是公共的并将常量值传递给超类,从而使参数对超类的用户不可见子类。

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

Also, I don't want to give the value
of the field as an argument of the
constructor.

Why not? It's the perfect solution. Make the constructor protected and offer no default constructor, and subclass implementers are forced to supply a value in their constructors - which can be public and pass a constant value to the superclass, making the parameter invisible to users of the subclasses.

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);
    }
}
茶花眉 2024-09-04 17:57:23

使用模板模式怎么样?

public abstract class Template {

    private String field;

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

    abstract String init();
}

通过这种方式,您可以强制所有子类实现 init() 方法,由于该方法是由构造函数调用的,因此将为您分配字段。

How about using the Template pattern?

public abstract class Template {

    private String field;

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

    abstract String init();
}

In this way, you force all subclasses to implement the init() method, which, since it being called by the constructor, will assign the field for you.

岁月如刀 2024-09-04 17:57:23

您不能在构造函数中执行此操作,因为超类将在子类中的任何内容之前初始化。因此,在超级构造函数中访问特定于子类的值将会失败。
考虑使用工厂方法来创建对象。例如:

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 类来创建此抽象类的所有变体,并且您可以将字段传递到构造函数中。您的工厂将是唯一了解该领域的工厂,而工厂的用户可能对此一无所知。

编辑:即使没有工厂,您也可以使抽象基类需要构造函数中的字段,以便所有子类在实例化时都必须向其传递一个值。

You can't do this in the constructor since the super class is going to be initialized before anything in the subclass. So accessing values that are specific to your subclass will fail in your super constructor.
Consider using a factory method to create your object. For instance:

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

You have an issue in this particular sample where MyClass can't be subclassed, but you could make the constructor protected. Make sure your base class has a public / protected constructor also for this code. It's just meant to illustrate you probably need two step initialization for what you want to do.

Another potential solution you could use is using a Factory class that creates all variants of this abstract class and you could pass the field into the constructor. Your Factory would be the only one that knows about the field and users of the Factory could be oblivious to it.

EDIT: Even without the factory, you could make your abstract base class require the field in the the constructor so all subclasses have to pass in a value to it when instantiated.

哥,最终变帅啦 2024-09-04 17:57:23

此外,我不想将字段的值作为构造函数的参数给出。

有什么解决方案可以做到这一点,或者我应该改变我的设计吗?

是的,我认为你应该改变你的设计,以便子类将值传递给构造函数。由于对象的子类部分直到超类构造函数返回之后才被初始化,因此实际上没有其他干净的方法来执行此操作。当然,这是可行的:

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

这不会执行您的操作d 希望它如此,因为子类的初始化程序和构造函数在超类的初始化程序和构造函数之后运行。 new Sub2()new Sub3(42) 都会打印 Abstract field: 0 因为 value 字段没有调用 abstractField() 时尚未初始化。

将值传递给构造函数还有一个额外的好处,即存储值的字段可以是final

Also, I don't want to give the value of the field as an argument of the constructor.

Is there any solution to do that, or should I just change my design ?

Yes, I think you should change your design so that the subclass passes the value to the constructor. Since the subclass portion of your object isn't initialized until after the superclass constructor has returned, there's really no other clean way of doing it. Sure, this'd work:

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

... since the implementation of abstractField() doesn't operate on object state. However, you can't guarantee that subclasses won't think it's a great idea to be a little more dynamic, and let abstractField() returns a non-constant value:

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

This does not do what you'd expect it to, since the initializers and constructors of subclasses run after those of the superclass. Both new Sub2() and new Sub3(42) would print Abstract field: 0 since the value fields haven't been initialized when abstractField() is called.

Passing the value to the constructor also has the added benefit that the field you store the value in can be final.

卸妝后依然美 2024-09-04 17:57:23

如果该值是由子类的类型决定的,那么为什么还需要一个字段呢?您可以使用一个简单的抽象方法来为每个子类返回不同的值。

If the value is determined by the type of subclass, why do you need a field at all? You can have a simple abstract method which is implemented to return a different value for each subclass.

请别遗忘我 2024-09-04 17:57:23

我认为你需要一个可以作用于该参数的工厂(又名“虚拟构造函数”)。

如果用某种语言很难做到这一点,那么你的想法可能是错误的。

I think you need a factory (aka "virtual constructor") that can act on that parameter.

If it's hard to do in a given language, you're probably thinking about it incorrectly.

终止放荡 2024-09-04 17:57:23

如果我理解正确的话:您希望抽象类的构造函数根据抽象类中的字段执行某些操作,但该字段(希望)由子类设置?

如果我答错了,你可以停止阅读……

但如果我答对了,那么你就是在尝试做一些不可能的事情。类的字段按词法顺序实例化(因此,如果您在构造函数“下方”或“之后”声明字段,则在调用构造函数之前不会实例化这些字段)。此外,JVM 在对子类执行任何操作之前都会运行整个超类(这就是为什么子类构造函数中的“super()”调用需要成为构造函数中的第一条指令……因为这仅仅是“建议” JVM 如何运行超类的构造函数)。

因此,只有在超类完全实例化(并且超类的构造函数已返回)之后,子类才开始实例化。

这就是为什么你不能有抽象字段:抽象字段不会存在于抽象类中(而只存在于子类中),因此对于超级(抽象)类来说是严重(!)“禁止”的......因为 JVM 无法将任何引用绑定到该字段(因为它不存在)。

希望这有帮助。

If I understand you correctly: You want the abstract class's constructor to do something depending on a field in the abstract class but which is set (hopefully) by the subclass?

If I got this wrong you can stop reading ...

But if I got it right then you are trying to do something that is impossible. The fields of a class are instantiated in lexical order (and so if you declare fields "below", or "after", the constructor then those will not be instantiated before the constructor is called). Additionally, the JVM runs through the entire superclass before doing anything with the subclass (which is why the "super()" call in a subclass's constructor needs to be the first instruction in the constructor ... because this is merely "advice" to the JVM on how to run the superclass's constructor).

So a subclass starts to instantiate only after the superclass has been fully instantiated (and the superclass's is constructor has returned).

And this is why you can't have abstract fields: An abstract field would not exist in the abstract class (but only in the subclass) and so is seriously(!) "off limits" to the super (abstract) class ... because the JVM can't bind anything references to the field (cause it doesn't exist).

Hope this helps.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文