4

我正在查看游戏中的一些代码,我遇到了一些我以前从未见过的东西,我真的不知道发生了什么。

public abstract class Entity
{

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

    protected abstract void entityInit();
}

这里发生了什么?当它调用时会发生什么entityInit()

4

7 回答 7

9

抽象类永远不会被实例化。只有它的具体子类可以被实例化。因此,当调用具体的子类(我们称之为Foo)构造函数时,它会调用super(world). Entity 构造函数然后调用entityInit(),它已被 覆盖Foo。因此它调用 FooentityInit具体方法。

请注意,这是不好的做法,因为该entityInit方法将在尚未完全构造的对象上调用。因此,子类必须确保此方法不访问它可能声明的任何字段,因为它们都将被初始化。

于 2011-10-04T07:13:46.787 回答
3

entityInit()发生的情况是调用了具体子类的实现。由于Entity是抽象的,它的构造函数只能从具体子类的构造函数中调用,该构造函数必须具有entityInit().

从抽象类的代码中调用抽象方法实际上是非常常见的,并且几乎是抽象方法的全部要点。另请参阅模板方法模式

但是,在这种情况下从构造函数调用抽象方法是有问题的,应该避免,主要是因为抽象方法将在未完全初始化的对象上运行,因此可能处于不一致的状态。具体来说, 的实现entityInit()将在定义它的类的构造函数之前运行。

于 2011-10-04T07:11:09.770 回答
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);
    }
}
于 2011-10-04T07:19:22.717 回答
1

好吧,没什么。

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

于 2011-10-04T07:10:01.443 回答
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仍然为空。

于 2011-10-04T07:10:07.723 回答
1

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

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

于 2011-10-04T07:10:07.697 回答
0

不要从构造函数中调用抽象/虚拟方法。它将调用具体的子类实现。但是具体子类的成员变量不会初始化。

另一种说法是不要让你的this逃离构造函数 body

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

于 2011-10-04T07:12:21.500 回答