Java继承中super与override的使用场景及代码复用问题
Hey there! Let's tackle that duplicate code problem you're facing with your Attack and UseForce classes. I'll walk you through a few practical approaches, including using inheritance properly with super/override, and how to extract duplicate code even when you're already tied to a parent class.
1. Reduce Act Method Duplication with Inheritance
The biggest win here is fixing the act() method—most of the logic is identical, only the energy value differs. Here's how to refactor this cleanly:
Step 1: Update the Parent Attack Class
Make the energy value a protected abstract method (or a protected field) so subclasses can provide their own value, while keeping all shared act() logic in the parent class:
public class Attack extends SWAffordance implements SWActionInterface { // ... existing code ... // Define an abstract method for subclasses to supply required energy protected abstract int getRequiredEnergy(); @Override public boolean canDo(SWActor a) { SWEntityInterface target = this.getTarget(); return !a.isDead() && target.getHitpoints() > 0; } @Override public void act(SWActor a) { SWEntityInterface target = this.getTarget(); boolean targetIsActor = target instanceof SWActor; SWActor targetActor = null; // Use the abstract method to get the energy value int requiredEnergy = getRequiredEnergy(); if (targetIsActor) { targetActor = (SWActor) target; } // ... rest of your shared act() logic goes here ... } }
Step 2: Simplify the UseForce Subclass
Now UseForce only needs to override the energy method (and adjust canDo() if needed)—no more repeating the entire act() method:
public class UseForce extends Attack { private int minUsePoints; // Assuming this is defined in your class // ... existing code ... // Provide the force-specific energy value @Override protected int getRequiredEnergy() { return 2; } @Override public boolean canDo(SWActor a) { // If you need to include the parent's canDo logic too, use super.canDo(a) // return super.canDo(a) && a.getForcepoints() >= minUsePoints; // For your current requirement, keep your original check: return a.getForcepoints() >= minUsePoints; } }
If you prefer using a field instead of an abstract method, initialize it in constructors:
// In Attack.java protected int requiredEnergy; public Attack() { this.requiredEnergy = 1; // Default for Attack } // In UseForce.java public UseForce() { this.requiredEnergy = 2; // Override for UseForce }
Then use this.requiredEnergy in the act() method instead of the abstract method.
2. Extracting Duplicate Code with a Helper Class (When Inheritance Isn't Enough)
Since Attack already inherits from SWAffordance, you can't extend another class—but you can use composition with a helper class to extract shared logic. For example, create a utility class to handle target preparation:
public class AttackTargetHelper { // Extract target casting logic into a reusable method public static SWActor getTargetAsActor(SWEntityInterface target) { return target instanceof SWActor ? (SWActor) target : null; } // Or a method that handles the entire common setup public static void prepareAttackContext(SWEntityInterface target, Consumer<SWActor> targetAction) { SWActor targetActor = getTargetAsActor(target); // Add any other shared setup logic here if (targetActor != null) { targetAction.accept(targetActor); } } }
Then use this in both classes to avoid repetition:
// In Attack.act() @Override public void act(SWActor a) { SWEntityInterface target = this.getTarget(); int requiredEnergy = 1; AttackTargetHelper.prepareAttackContext(target, (targetActor) -> { // Your attack-specific logic here }); // ... rest of shared logic ... } // In UseForce.act() @Override public void act(SWActor a) { SWEntityInterface target = this.getTarget(); int requiredEnergy = 2; AttackTargetHelper.prepareAttackContext(target, (targetActor) -> { // Your force-specific logic here }); // ... rest of shared logic ... }
This keeps duplicate code centralized in one place, making updates easier later.
3. When to Use super vs override
Let's break down the use cases clearly:
override: Use this when you want to replace or extend the parent class's method behavior. For example,UseForceoverridescanDo()to add a force points check, or overridesgetRequiredEnergy()to provide a different value. Always add the@Overrideannotation—it catches typos and ensures you're actually overriding a parent method.super: Use this when you want to reuse the parent class's method logic inside your subclass's overridden method. For example, ifUseForce'scanDo()should also validate the actor is alive and the target has hitpoints (fromAttack.canDo()), you'd write:
You can call@Override public boolean canDo(SWActor a) { // First run the parent's canDo check, then add our own condition return super.canDo(a) && a.getForcepoints() >= minUsePoints; }super.method()anywhere in your subclass method—start, middle, or end—depending on when you need the parent's logic to run.
内容的提问来源于stack exchange,提问作者Sook Lim

