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 AbstractEntity
es 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 amoveTo(Target)
method, as well as agetQueuedActions
method. A subclass can't 'remove' this. - Any types that
extends
MovableEntity must provide an implementation of thegetQueuedActions
method - failure to do so is a compiler error. Unless they are markedabstract
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 ofmoveTo(Target)
- even if it does, an impl ofgetQueuedActions
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 invokinggetQueuedActions
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.