26) Class inheritance versus interface inheritance
Last updated
Last updated
Class inheritance scores better when you want to reuse the implementation already defined in a 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.
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:
You may prefer interface inheritance over class inheritance when you need to define multiple contracts for classes
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:
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.
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: