0

I have a two abstract classes FlyingBird and RunningBird

public abstract FlyingBird extends Bird implements IFlyingBird {}
public abstract RunningBird extends Bird implements IRunningBird {}

Now I want to have an another abstract class that is both IRunningBird and IFlyingBird

public abstract FlyingRunningBird extends Bird implements IFlyingBird, IRunningBird {}

In this class I am duplicating all the code from FlyingBird and RunningBird
Aggregation of dependent class is not possible as both classes are abstract

The issue is if any bug fix or modification that is done on either FlyingBird or RunningBird will not reflect in FlyingRunningBird. I need to update the FlyingRunningBird too.

Is there any we can avoid this code duplication in FlyingRunningBird? or
Use FlyingBird and RunningBird in FlyingRunningBird to avoid code duplication?

2
  • If code is shared between FlyingBird and RunningBird, it likely belongs to the Bird class. Commented Jan 6, 2022 at 9:35
  • You can't extend 2 classes in Java, so the answer probably revolves around migrating FlyingBird and RunningBird to interfaces. Commented Jan 6, 2022 at 9:39

3 Answers 3

2

Java was designed with the problems of multiple inheritance in C++ in mind. So only single inheritance and multiple interfaces. At that time in C++ there could be ambiguity of a method that could belong to two parent classes.

To achieve your goal, the solution is to use delegating:

  • specify interfaces
  • specify implementations
  • delegate

Using an other conventional naming:

public interface Flying {
    void fly();
    double getWingSpan();
}

public class FlyingImplementation implements Flying {
    public FlyingImplementation(Bird bird)
    ...
}

public interface Running {
    void run();
    double getRunningSpeed();
}

public class RunningImplementation implements Running {
    public RunningImplementation(Bird bird) {}
    ...
}

public abstract FlyingRunningBird implements Flying, Running, Bird {
    private final Flying birdFlying;
    private final Running birdRunning;

    protected FlyingRunningBird() {
        birdFlying = new FlyingImplementation(this);
        birdRunning = new RunningImplementation(this);
    }

    @Override
    public void fly() {
        birdFlying.fly();
    }

    @Override
    void run() {
        birdRunning.run();
    }
    ...
}

The common things in Bird, which also could have a base class. Flying and Running are capabilities, names adjectives, sole responsibility: flying resp. running.

Delegating to birdFlying and birdRunning makes the "multiple inheritance" explicit and is by no means less expressive.

Sign up to request clarification or add additional context in comments.

Comments

1

I think there are two ways you can approach this:

  1. Move the method implementations as default methods in the corresponding interface, given that you're using Java 8+ and those methods do not need to access fields (in which case you'll need to have some accessors in your interface or reconsider your design).
  2. Favor composition over inheritance, though in this particular case this doesn't seem quite natural. I.e. your FlyingRunningBird could be provided with an instance of FlyingBird and an instance of RunningBird (at construction time), and it will simply delegate the method calls for each of the interfaces to the corresponding implementation.

2 Comments

"your FlyingRunningBird could be provided with an instance of FlyingBird" or, extract the "flyingness" and "runningness" into separate classes, and provide an instance of "flyingness" to both FlyingBird and FlyingRunningBird, and provide an instance of "runningness" to RunningBird and FlyingRunningBird. In this way, you continue to have the correct number of instances of Bird (you've not got "two kids in a trenchcoat")
@AndyTurner true, and that's especially useful since "flyingness" and "runningness" as "traits" can then be applied to entities other than birds, so the OP would get true separation of concerns with proper/orthogonal hierarchies.
0

In your case, you may use Helper classes -> classes that do not fall anywhere in the hierarchy but have the common code to avoid duplicacy.

Otherwise, from the OO point of view -

  1. A FlyingRunningBird may not fly the same as a FlyingBird.
  2. A FlyingRunningBird may not fly the same as a RunningBird

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.