Home > Software design >  Is there an equivalent to #implementedBySubclass in Java?
Is there an equivalent to #implementedBySubclass in Java?

Time:07-18

I have a design question. I have a class hierarchy:

  • AbstractEntity
  • NPC extends AbstractEntity implements LifeForm
  • Player extends AbstractEntity implements LifeForm

There is a method #moveTo(Target target). This method exists currently in both Player and NPC.

The implemetation is the same for both classes. Only one method called (getQueuedActions()) has a different implementation.

So, in theory, the whole method can be implemented at AbstractEntity, and the concrete difference is implemented at the child class level. But the Java compiler says: getQueuedActions() needs to be implemented also at AbstractEntity. I could of course do an instace of check and implement accordinly. But I thought I ask here, for I think I have something wrong here.

Is there a way to implement the method in Java indicating that yes, this does exist at AbstractEntity but look at the concrete (sub-)class for the real implementation? In Smalltalk, you would do ^implementedBySubClass.

Is there an equivalent in Java?

CodePudding user response:

If you put moveTo(Target) in AbstractEntity that would mean all AbstractEntityes can be moved. All of them. You can't not: That's what putting it at that level means.

If NPC can be moved, and Player can be moved, but let's say Tree (also an AbstractEntity) cannot, then moveTo should NOT be in AbstractEntity at all!

Possibly you want something in between; perhaps Person extends MovableEntity and NPC extends MovableEntity, and MovableEntity extends AbstractEntity. And moveTo can go in MovableEntity.

Whether it's MovableEntity or AbstractEntity that gets a moveTo(Target target) method - you are free to specify an implementation or not (the act of saying: "All Xs have the Y method" is separate from "... and here is the code for that method". You choose: Either do the first (decree that the method must be there, but not what the code for it is), or both (decree that the method must be there, and provide an implementation, which a subclass is free to override with an alternate implementation unless you want to disable that, which you can, by marking the method with the final keyword).

That implementation is free to use any other property of the type it is in - including properties that are of the 'The type decrees this property exists, not what it looks like' variety. This is perfectly legal java:

class MovableEntity extends AbstractEntity {
  public void moveTo(Target target) {
    var actions = getQueuedActions();
    .... move to and do the actions ....
  }

  public abstract List<Action> getQueuedActions();
}

This class definition says:

  • You can't instantiate me; I am abstract. My only reason for existing is to serve as common supertype for other more specific types. If nobody ever writes class Something extends MovableEntity, this code is completely useless.
  • All MovableEntity instances regardless of its actual type have a moveTo(Target) method, as well as a getQueuedActions method. A subclass can't 'remove' this.
  • Any types that extends MovableEntity must provide an implementation of the getQueuedActions method - failure to do so is a compiler error. Unless they are marked abstract which is fine (in which case the requirement to have that method is passed on to any subclasses of it in turn).
  • Any types that extends MovableEntity may provide its own implementation of moveTo(Target) - even if it does, an impl of getQueuedActions must still be provided, that rule doesn't change. If it doesn't provide an implementation, it inherits the implementation as provided here (which ends up invoking getQueuedActions as part of how it works).

Possibly ^implementedBySubclass is smalltalk-ese for marking a method as abstract. You should in general, when asking on SO questions in the vein of "This thing in language X - is there an equivalent to that in language Y?", explain what 'this thing' actually means.

Especially for SmallTalk: An effectively dead language that never had many users and whose docs are all locked away behind the paywalls of bankrupt companies. All one can find is multiple notes on how IBM/Smalltalk called a thing implementedBySubclass where squeak called it subclassResponsibility, and the oft-repeated maxim that the entire smalltalk spec fits on a business card (I checked the business card. It doesn't mention implementedBySubclass anywhere on the card). That's about all I could find about what it specifically means.

CodePudding user response:

This is the difference between a strongly-typed language like Java and a dynamically-typed language like Smalltalk.

In Smalltalk, you can rely on convention to glue things together, and if something breaks, the developer needs to debug it and figure out why (they'd likely see an error with #subclassResponsibility).

Java needs to know at complile time what classes can or can't do. So in your case, it's complaining that you're expecting your abstract class to understand/implement a method, but only the subclasses understand it - and you could in future be adding other concrete subclasses whose instances don't implement the method.

So one approach you could take in Java would be to implement the method in the abstract superclass, but throw an exception - either a custom one, or something like java.lang.UnsupportedOperationException. The subclasses can then override that method to provide their specific implementations. That way, each instance behaves correctly, and the compiler is happy. If you were to add a new concrete subclass that doesn't override the method, you'd get your exception at runtime if you tried to call that method - which would tell you that you need to implement it.

  • Related