سؤال

I'm looking over some code in a game and I came across something that I haven't seen before and I don't really know whats going on.

public abstract class Entity
{

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

    protected abstract void entityInit();
}

What's going on here? What happens when it calls on entityInit()?

هل كانت مفيدة؟

المحلول

An abstract class is never instantiated. Only its concrete subclasses can be instantiated. So, when the concrete subclass (let's call it Foo) constructor is called, it calls super(world). The Entity constructor then calls entityInit(), which has been overridden by Foo. It thus calls the Foo entityInit concrete method.

Note that this is bad practice, because the entityInit method will be called on a not yet fully constructed object. The subclass thus has to make sure this method doesn't access any field it might declare, because they will all be unititialized.

نصائح أخرى

What happens is that the concrete subclass's implementation of entityInit() gets called. Since Entity is abstract, its constructor can only ever be called from a concrete subclass's constructor, which must have an implementation of entityInit().

Calling abstract methods from within code of the abstact class is actually perfectly common, and pretty much the entire point of abstract methods. See also the template method pattern.

However, calling abstract methods from a constructor as in this case is problematic and should be avoided, mainly because the abstract method will run on an object that is not completely initialized and thus possibly in an inconsistent state. Specifically, the implementation of entityInit() will run before the constructor of the class it's defined in.

Well, mostly, this is used in Template Method pattern. The subclassed, non-abstract class of Entity will implement the entityInit() method. These subclasses implement the way their entityInit() must be defined for that class.

In Wikipedia, it says....

In object-oriented programming, first a class is created that provides the basic steps of an algorithm design. These steps are implemented using abstract methods. Later on, subclasses change the abstract methods to implement real actions. Thus the general algorithm is saved in one place but the concrete steps may be changed by the subclasses.

In your case, you won't worry about subclasses having to entityInit() itself, as the constructor will do this by default:

Example:

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

Well, nothing.

Unless a concrete class implements the abstract method entityInit, you won't be able to create an Entity class that will use the method.

Since you need to create a concrete subclass anyways you need to implement entityInit() as well. That method will then be called.

You can't create instances of abstract classes and concrete class (which you can create instances of) must not have abstract methods. So everything is fine.

One note: be aware that if you access fields defined in the subclass, accessing them in entityInit() might result in a NullPointerException since they might not be initialized.

Example (based on your class):

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

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

Although that example doesn't make much logical sense, it should illustrate the point. First the Entity constructor will be called, which in turn calls entityInit(). However, since the initializer block of Person has not run yet, name is still null.

it's a common practice. You use the abstract method in a top level method, implementors will only need to implement the abstract, so logic will be left in base class.

Sorry haven't noticed it was a constructor... pretty strange in that position....

Don't call abstract/virtual methods from the constructor. it would call the concrete subclass implementation. But the concrete subclass's member variables would have not initialized.

Another way to say that don't let your this escape from the constructor body.

But in this particular case, the whole purpose of the method is to initialize the members of the this object. So it's fine. but with this approach, we have to chain the method invocation of super class( if it has the implementation ).

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top