dcsimg
August 6, 2020
Hot Topics:

The Intricacies of Multiple Inheritance in Java

  • By Manoj Debnath
  • Send Email »
  • More Articles »

Inheritance is one of the primary capabilities of object-oriented programming (OOP) languages, such as Java. It is a fundamental technique for organizing classes in a manner that enhances the capability of class reuse in software design. Multiple inheritance is one of the many types of inheritance and is an important tenet of the inheritance mechanism. But, it is notorious for creating ambiguous relationships among classes. Therefore, Java designers discarded this idea, although not completely. This article explores some of the concepts of inheritance in general along with the intricacies of multiple inheritance in Java.

Overview

Inheritance creates a hierarchy of subclasses where the subclasses extend the capabilities of the parent class. This is done not only with the idea to inherit the capabilities of the super class, but also to give a new meaning by the inherited class. This extension of capabilities is typically done by overriding the functionalities of the super class, adding new methods and properties. In Java, there is no practical limit on number of subclasses that can be derived from the super class. But, the hierarchy must follow a linear fashion.

Therefore, when creating a subclass, instead of declaring all new class members, we designate it to use the members of existing class. The existing class is called the super class and the new class is called the subclass where each subclass has the capability to become a superclass in further extension in inheritance hierarchy. This hierarchy is shown in Figure 1.

A UML diagram to illustrate inheritance
Figure 1: A UML diagram to illustrate inheritance (Courtesy, UML Diagram Tool by Creately)

Types of Inheritance

There are many types of inheritance supported by various object-oriented programming languages in general, such as single-level inheritance, multi-level inheritance, multiple inheritance, multipath inheritance, hierarchical, and hybrid inheritance. Note that these type differences are purely academic; OOP languages use one or more of these types in practice.

  • Single-level inheritance: Here, the subclass is derived from a single super class.
  • Multi-level inheritance: This is an extension of single-level inheritance, but each subclass has the capability to be a superclass of future subclasses. Every inheritance maintains a linear path.
  • Multiple inheritance: Here, the subclass is derived from multiple base classes. Java does not support this type of inheritance, but C++ supports it.
  • Multipath inheritance: This is an extension of multiple inheritance where a subclass is derived from a base class and from multiple subclasses of the base class. As should be obvious, this type of inheritance also is not supported by Java.
  • Hierarchical inheritance: In this type of inheritance, more than one derived class can be created from a single super class and the process can be extended in a tree structure.
  • Hybrid inheritance: This is nothing but a combination of more than one type of inheritance.

Multilevel Single Inheritance in Java

Java supports single inheritance where each class is derived from exactly one direct superclass. Each subclass derived has the potential to be a super class of future subclasses. In single level inheritance, the subclasses inherit the properties of the parent class. We also can create multiple subclasses from a single parent class which further may be a subclass of another parent class. Therefore, multilevel single inheritance essentially means that we can extend the idea of single-level class hierarchy to multiple levels.

A Quick Example

This example is a quick implementation of the UML diagram illustrated in Figure 1.

package com.mano.examples;
public class Company {
   public static void main(String[] args) {
      Staff staff = new Staff();
      staff.pay();
   }
}

package com.mano.examples;
import java.util.ArrayList;
import java.util.List;
public class Staff {
   private List<StaffMember> staffMembers = new ArrayList<>();
   public Staff(){
      staffMembers.add(new ExecutiveEmployee
         ("Mickey","Disney Land","1234","987654321",1236.56));
      staffMembers.add(new Employee
         ("Donald","Disney Land","2233","87459985",5603.48));
      staffMembers.add(new RegularEmployee
         ("Zairo","Disney Land","2244","87452658",12.36));
      staffMembers.add(new Apprentice
         ("Minnie","Disney Land","963258741"));
      ((ExecutiveEmployee)staffMembers.get(0)).bonusReceived(1200);
      ((RegularEmployee)staffMembers.get(2)).updateHours(5);
   }
   public void pay(){
      for(StaffMember m: staffMembers){
         System.out.println(m.toString());
         System.out.println("Earnings: "+ m.earnings());
         System.out.println("------------------------------");
      }
   }
}

package com.mano.examples;
abstract public class StaffMember {
   protected String name;
   protected String address;
   protected String phone;
   public StaffMember(String n, String a, String p){
      name = n;
      address = a;
      phone = p;
   }
   @Override
   public String toString() {
      return "StaffMember{" +
          "name='" + name + '\'' +
          ", address='" + address + '\'' +
          ", phone='" + phone + '\'' +
          '}';
   
   }
   public abstract double earnings();
}

package com.mano.examples;
public class Employee extends StaffMember {
   protected String empId;
   protected double rate;
   public Employee(String n, String a, String p,String id,
         double r){
      super(n,a,p);
      empId = id;
      rate = r;
   }
   @Override
   public String toString() {
      return "Employee{" +
         "empId='" + empId + '\'' +
         ", rate=" + rate +
         super.toString() +
         '}';
   }
   @Override
   public double earnings() {
      return rate;
   }
}

package com.mano.examples;
public class Apprentice extends StaffMember {
   public Apprentice(String n, String a, String p){
      super(n,a,p);
   }
   public double earnings(){
      return 0.0;
   }
   @Override
   public String toString() {
      return super.toString();
   }
}

package com.mano.examples;
public class ExecutiveEmployee extends Employee {
   private double bonus;
   public ExecutiveEmployee(String n, String a, String p,
         String id, double r){
      super(n,a,p,id,r);
      bonus = 0;
   }
   public void bonusReceived(double b){
      bonus = b;
   }
   public double earnings(){
      return super.earnings() + bonus;
   }
   @Override
   public String toString() {
      return "ExecutiveEmployee{" +
         "bonus=" + bonus + super.toString()+
         '}';
   }
}

package com.mano.examples;
public class RegularEmployee extends Employee{
   private int hoursWorked;
   public RegularEmployee(String n, String a, String p,
         String id, double r){
      super(n,a,p,id,r);
      hoursWorked = 0;
   }
   public void updateHours(int h){
      hoursWorked+=h;
   }
   public double earnings(){
      return rate*hoursWorked;
   }
   @Override
   public String toString() {
      return "RegularEmployee{" +
         "hoursWorked=" + hoursWorked +super.toString()+
         '}';
   }
}

Multiple Inheritance

Java does not support multiple inheritance. Multiple inheritance means a class derived from more than one direct super class. This increases complexities and ambiguity in the relationship among classes. The problem is clearly visible if we consider what happens in function overriding. Suppose there are two classes, A and B, each defining a function called func(). Now, let's say we define another class, C, which inherits both from A and B (multiple inheritance), but let's say this class does not override the function called func().

Now, if we do something like the following:

C c = new C();
c.func();

Can we determine which member function func() is invoked? Is it function defined by class A or class B? As we can see, the class C inherits this function doubly from both A and B. This creates ambiguity and the compiler is in a fix to resolve the issue. There is solution to this problem, but it's extremely complex; therefore, Java decided to stay away from the cause of the problem by not allowing multiple inheritance.

Multiple Inheritance with Interfaces

Although Java does not allow multiple inheritance, it allows a multiple implementation of interfaces. So, in a way the idea is still there. Now, what is an interface?

An interface describes a set of methods but does not provide any concrete implementation for all methods. We can implement the interface with the help of a class which provides the concrete implementation of the method. In this way, a class can implement more than one interface and therefore can provide a concrete implementation of methods derived from one or more interfaces. The class that implements one or more interfaces forms a is-a relationship with the interface type. This also means that objects instantiated from the class are guaranteed to provide the functionality declared by the interface. Any subclass derived from this class also provides the same functionality.

Interfaces are especially useful for providing a common functionality to a number of possibly unrelated classes. Therefore, the object of classes that implement same interface can respond to method calls described in all of the interfaces.

From Java 8, interfaces support default methods with its full implementation. As we know, a class can implement more than one interface; therefore, if multiple interfaces contain a default method with the same method signature, the implemented class should specify which particular method to use or override.

A Quick Example

package com.mano.examples;
public interface Interface1 {
   default void func(){
      System.out.println("from interface 1");
   }
}

package com.mano.examples;
public interface Interface2 {
   default void func(){
      System.out.println("from interface 2");
   }
}

package com.mano.examples;
public class MyClass implements Interface1, Interface2 {
   public void func(){
      // Invoking Interface1 func() method
      Interface1.super.func();
      // Invoking Interface2 func() method
      Interface2.super.func();
   }
   public static void main(String[] args){
      MyClass c=new MyClass();
      c.func();
   }
}

Conclusion

One of the classic problems with multiple inheritance is called the diamond problem. This can be tackled with the inheritance mechanism called virtual inheritance. But, experience says Java has not lost much by disallowing multiple inheritance altogether. In fact, the Java compiler has gotten rid of them by providing a complex solution to a problem that we can easily do away with. Still, there are OOP languages, such as C++, which support it.






This article was originally published on February 19, 2020

Enterprise Development Update

Don't miss an article. Subscribe to our newsletter below.


Thanks for your registration, follow us on our social networks to keep up-to-date