java 如何创建“抽象字段”?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2613596/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-29 22:01:17  来源:igfitidea点击:

How to create an "abstract field"?

javaconstructorabstract-class

提问by Jules Olléon

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

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

Problem

问题

I have an abstract class that does an operation in the constructordepending 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)

(子类是 Hand、Pencil 和 AddObject,它们都扩展了抽象类 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.

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

回答by Crozin

How about abstract getter/setter for field?

字段的抽象 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";
    }
}

回答by Michael Borgwardt

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 protectedand 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.

为什么不?这是完美的解决方案。创建构造函数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);
    }
}

回答by matsev

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.

通过这种方式,您强制所有子类实现该init()方法,因为它被构造函数调用,将为您分配字段。

回答by Peter Lawrey

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.

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

回答by gustafc

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 afterthe 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:

...因为 的实现abstractField()不对对象状态进行操作。但是,您不能保证子类不会认为多一点动态是个好主意,并且 letabstractField()返回一个非常量值:

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: 0since the valuefields haven't been initialized when abstractField()is called.

这不会像您期望的那样做,因为子类的初始化器和构造器在超类的初始化器和构造器之后运行。双方new Sub2()new Sub3(42)会打印Abstract field: 0,因为value当领域尚未初始化abstractField()被调用。

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

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

回答by NG.

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.

在此特定示例中,您遇到了一个问题,即无法对 MyClass 进行子类化,但您可以保护构造函数。确保您的基类也具有用于此代码的公共/受保护构造函数。这只是为了说明您可能需要两步初始化才能完成您想要做的事情。

您可以使用的另一个潜在解决方案是使用 Factory 类,该类创建此抽象类的所有变体,并且您可以将该字段传递给构造函数。您的工厂将是唯一了解该领域的工厂,而工厂的用户可能对此一无所知。

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

回答by duffymo

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.

如果用一种给定的语言很难做到,你可能想错了。

回答by Vilnis Krumins

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).

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

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

所以子类只有在超类完全实例化后才开始实例化(并且超类的 is 构造函数已经返回)。

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).

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

Hope this helps.

希望这可以帮助。