26) Class inheritance versus interface inheritance

1) Comparing class inheritance and interface inheritance:

2) Preferring class inheritance over interface inheritance:

Class inheritance scores better when you want to reuse the implementation already defined in a base class.

REUSING THE IMPLEMENTATION FROM THE BASE CLASS:

When we create any class, we extend and reuse class java.lang.Object. The class Object defines code to take care of all the threading and object-locking issues, together with providing default implementation for methods like toString(), hashCode(), and equals(). Method toString() returns a textual description (String) of an instance. Methods like hashCode() and equals() enable objects to be stored and retrieved efficiently in hash-based collection classes like HashMap. What do you think would happen if class java.lang.Object was defined as an interface? In this case, you’d need to implement all these methods for every class that you created. But it’s not useful to replicate this type of boilerplate code across many implementation classes. So class inheritance comes in handy here.

ADDING NEW BEHAVIOR IN ALL DERIVED CLASSES

Imagine you created a set of entities (Lion, Elephant), identified their common behavior, and moved the common behavior to their common base class (Animal). Because you control the definition of all these classes, you might add new behavior to your base class and make it available to all the derived classes. Examine the following definition of the abstract class Animal and nonabstract class Lion, which extends class Animal:

public abstract class Animal {
public abstract void move();
public abstract void live();
}
public class Lion extends Animal {
public void move(){/*...*/}
public void live(){/*...*/}
}

3) Preferring interface inheritance over class inheritance:

You may prefer interface inheritance over class inheritance when you need to define multiple contracts for classes

IMPLEMENTING MULTIPLE INTERFACES

Imagine you need to use a class that can be executed in a separate thread and can be attached as an ActionListener to a GUI component. You can achieve this by making your class implement multiple interfaces that support these functionalities—interfaces Runnable and ActionListener:

class MyClass implements Runnable, ActionListener {
//..code to implement methods from interface
//..Runnable and ActionListener
}

Interface implementation has one major advantage: a class can implement multiple interfaces, to support multiple functionality. For the preceding example, you can pass instances of class MyClass to all methods that define parameters of type Runnable or ActionListener.

FRAGILE DERIVED CLASSES

Adding to or modifying a base class can affect its derived classes. Adding new methods to a base class can result in breaking the code of a derived class. Consider this initial arrangement, which works well:

If a base class chooses to modify the implementation details of its methods, the derived classes might not be able to offer the functionality they were supposed to, or they might respond differently. Consider this initial arrangement:

Last updated