抽象类是否使用自己的抽象方法?

4
我正在查看一款游戏中的代码,发现了一些之前没见过的东西,不太清楚是怎么回事。
public abstract class Entity
{

    public Entity(World world)
    {
        // irrelevent code
        entityInit();
    }

    protected abstract void entityInit();
}

这里发生了什么?当调用entityInit()时会发生什么?
7个回答

9
一个抽象类永远不会被实例化,只有它的具体子类可以被实例化。因此,当具体子类(我们称之为Foo)的构造函数被调用时,它调用super(world)。然后Entity构造函数调用了被Foo覆盖的entityInit()方法,因此调用了Foo的entityInit具体方法。
请注意,这是一个不好的做法,因为entityInit方法将在尚未完全构建的对象上调用。因此,子类必须确保该方法不访问它可能声明的任何字段,因为它们都将未初始化。

哦,我明白了。这有点让我困惑(我对编程还很新),但这解决了问题。谢谢! - Austin Moore

3
发生的情况是调用具体子类的entityInit()实现。由于Entity是抽象的,它的构造函数只能从具体子类的构造函数中调用,而该构造函数必须有entityInit()的实现。
从抽象类中的代码调用抽象方法实际上非常普遍,并且几乎是抽象方法的全部意义所在。还可以参考模板方法模式
然而,在像这种情况下从构造函数中调用抽象方法是有问题的并且应该被避免的,主要是因为抽象方法将在尚未完全初始化的对象上运行,从而可能处于不一致的状态。具体地说,entityInit()的实现将在定义它的类的构造函数之前运行。

2
大多数情况下,这是在模板方法模式中使用的。非抽象类Entity的子类将实现entityInit()方法。这些子类实现了其entityInit()必须为该类定义的方式。
在维基百科上,它说...

在面向对象编程中,首先创建一个类,该类提供算法设计的基本步骤。这些步骤使用抽象方法实现。后来,子类更改抽象方法以实现实际操作。因此,一般算法保存在一个地方,但具体步骤可以由子类更改。

在您的情况下,您不必担心子类必须自己执行entityInit(),因为构造函数将默认执行此操作:
示例:
public class StrictEntity extends Entity {

    public StrictEntity(World world) {
        super(world); //This will call entityInit();
    }

    protected void entityInit() {
        //Example, don't take it as genuine.
        PropertyConfig.getInstance.setStrict(true);
    }
}

1

由于您仍需要创建具体的子类,因此您需要实现entityInit()方法。然后将调用该方法。

无法创建抽象类的实例,并且具体类(可以创建实例)不得具有抽象方法。 因此一切都很好。

请注意:如果您访问在子类中定义的字段,则在entityInit()中访问它们可能会导致NullPointerException,因为它们可能尚未初始化。

示例(基于您的类):

class Person extens Entity {
   private String name = "Player";

   protected void entityInit() {
     int nameLen = name.length(); //NPE here!!!
   }
}

虽然这个例子在逻辑上没有太多意义,但它应该能说明问题。 首先会调用Entity构造函数,然后调用entityInit()。然而,由于Person的初始化块尚未运行,因此name仍然为null。


1

没什么。

除非一个具体的类实现了抽象方法entityInit,否则你将无法创建一个使用该方法的Entity类。


好的,这正是我所想的,但我对一些事情不确定:编译器是否会寻找实现该方法的类?如果有多个扩展Entity并实现entityInt()的类,它会选择哪一个? - Austin Moore
@Eegabooga,不对,你有点理解错误。编译器并不是在查找实现它的类;作为编写该代码的用户,您将会实现这个抽象类,然后使用它。编译器并没有“猜测”的工作。 - darioo

1

这是一种常见的做法。您在顶层方法中使用抽象方法,实现者只需要实现抽象方法,因此逻辑将留在基类中。

抱歉,没有注意到它是一个构造函数... 在那个位置上相当奇怪....


0

不要在构造函数中调用抽象/虚方法。这会调用具体子类的实现,但是具体子类的成员变量可能尚未初始化。

另一种说法是不要让this从构造函数体中逃逸

但在这种特殊情况下,该方法的整个目的是初始化this对象的成员。所以没问题。但是使用这种方法,我们必须链接超类的方法调用(如果它有实现)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接