Tutorials  /  JavaScript

Multiple Inheritance in Java

Ccentron Redaktion · March 2024 ·7 min read ·JavaScript, Tutorial

Multiple inheritance in java is the capability of creating a single class with multiple superclasses. Unlike some other popular object oriented programming languages like C++, java doesn’t provide support for multiple inheritance in classes. Java doesn’t support multiple inheritances in classes because it can lead to diamond problem and rather than providing some complex way to solve it, there are better ways through which we can achieve the same result as multiple inheritances.

Diamond Problem in Java

To understand diamond problem easily, let’s assume that multiple inheritances were supported in java. In that case, we could have a class hierarchy like below image.

Diamond problem multiple inheritance

Let’s say SuperClass is an abstract class declaring some method and ClassA, ClassB are concrete classes. SuperClass.java

Go
package com.journaldev.inheritance;

public abstract class SuperClass {

    public abstract void doSomething();
}

ClassA.java

Go
package com.journaldev.inheritance;

public class ClassA extends SuperClass{
    
    @Override
    public void doSomething(){
        System.out.println("doSomething implementation of A");
    }
    
    //ClassA own method
    public void methodA(){
        
    }
}

ClassB.java

Go
package com.journaldev.inheritance;

public class ClassB extends SuperClass{

    @Override
    public void doSomething(){
        System.out.println("doSomething implementation of B");
    }
    
    //ClassB specific method
    public void methodB(){
        
    }
}

Now let’s say ClassC implementation would be something like below and it’s extending both ClassA and ClassB. ClassC.java

Go
package com.journaldev.inheritance;

// this is just an assumption to explain the diamond problem
//this code won't compile
public class ClassC extends ClassA, ClassB{

    public void test(){
        //calling super class method
        doSomething();
    }

}

Notice that test() method is making a call to superclass doSomething() method. This leads to the ambiguity as the compiler doesn’t know which superclass method to execute. Because of the diamond-shaped class diagram, it’s referred to as Diamond Problem in java. The diamond problem in Java is the main reason java doesn’t support multiple inheritances in classes. Notice that the above problem with multiple class inheritance can also come with only three classes where all of them has at least one common method.

VM

Matching infrastructure at centron

Node applications need somewhere to run: ccloud³ VMs with full root access from €3.12 per month – or as a managed server if you would rather not run it yourself. Rent a cloud server →

Multiple Inheritance in Java Interfaces

You might have noticed that I am always saying that multiple inheritances is not supported in classes but it’s supported in interfaces. A single interface can extend multiple interfaces, below is a simple example. InterfaceA.java

Go
package com.journaldev.inheritance;

public interface InterfaceA {

    public void doSomething();
}

InterfaceB.java

Go
package com.journaldev.inheritance;

public interface InterfaceB {

    public void doSomething();
}

Notice that both the interfaces are declaring the same method, now we can have an interface extending both these interfaces like below. InterfaceC.java

Go
package com.journaldev.inheritance;

public interface InterfaceC extends InterfaceA, InterfaceB {

    //same method is declared in InterfaceA and InterfaceB both
    public void doSomething();
    
}

This is perfectly fine because the interfaces are only declaring the methods and the actual implementation will be done by concrete classes implementing the interfaces. So there is no possibility of any kind of ambiguity in multiple inheritances in Java interfaces. That’s why a java class can implement multiple interfaces, something like below example. InterfacesImpl.java

Go
package com.journaldev.inheritance;

public class InterfacesImpl implements InterfaceA, InterfaceB, InterfaceC {

    @Override
    public void doSomething() {
        System.out.println("doSomething implementation of concrete class");
    }

    public static void main(String[] args) {
        InterfaceA objA = new InterfacesImpl();
        InterfaceB objB = new InterfacesImpl();
        InterfaceC objC = new InterfacesImpl();
        
        //all the method calls below are going to same concrete implementation
        objA.doSomething();
        objB.doSomething();
        objC.doSomething();
    }

}

Did you noticed that every time I am overriding any superclass method or implementing any interface method, I am using @Override annotation. Override annotation is one of the three built-in java annotations and we should always use override annotation when overriding any method.

Composition for the rescue

So what to do if we want to utilize ClassA function methodA() and ClassB function methodB() in ClassC. The solution lies in using composition. Here is a refactored version of ClassC that is using composition to utilize both classes methods and also using doSomething() method from one of the objects. ClassC.java

Go
package com.journaldev.inheritance;

public class ClassC{

    ClassA objA = new ClassA();
    ClassB objB = new ClassB();
    
    public void test(){
        objA.doSomething();
    }
    
    public void methodA(){
        objA.methodA();
    }
    
    public void methodB(){
        objB.methodB();
    }
}

Composition vs Inheritance

One of the best practices of Java programming is to “favor composition over inheritance”. We will look into some of the aspects favoring this approach.

Suppose we have a superclass and subclass as follows: ClassC.java

Go
package com.journaldev.inheritance;

public class ClassC{

    public void methodC(){
    }
}

ClassD.java

Go
package com.journaldev.inheritance;

public class ClassD extends ClassC{

    public int test(){
        return 0;
    }
}

The above code compiles and works fine but what if ClassC implementation is changed like below: ClassC.java

Go
package com.journaldev.inheritance;

public class ClassC{

    public void methodC(){
    }

    public void test(){
    }
}

Notice that test() method already exists in the subclass but the return type is different. Now the ClassD won’t compile and if you are using any IDE, it will suggest you change the return type in either superclass or subclass. Now imagine the situation where we have multiple levels of class inheritance and superclass is not controlled by us. We will have no choice but to change our subclass method signature or its name to remove the compilation error. Also, we will have to make a change in all the places where our subclass method was getting invoked, so inheritance makes our code fragile. The above problem will never occur with composition and that makes it more favorable over inheritance.

Another problem with inheritance is that we are exposing all the superclass methods to the client and if our superclass is not properly designed and there are security holes, then even though we take complete care in implementing our class, we get affected by the poor implementation of the superclass. Composition helps us in providing controlled access to the superclass methods whereas inheritance doesn’t provide any control of the superclass methods, this is also one of the major advantages of composition over inheritance.

Another benefit with composition is that it provides flexibility in the invocation of methods. Our above implementation of ClassC is not optimal and provides compile-time binding with the method that will be invoked, with minimal change we can make the method invocation flexible and make it dynamic. ClassC.java

Go
package com.journaldev.inheritance;

public class ClassC{

    SuperClass obj = null;

    public ClassC(SuperClass o){
        this.obj = o;
    }
    public void test(){
        obj.doSomething();
    }
    
    public static void main(String args[]){
        ClassC obj1 = new ClassC(new ClassA());
        ClassC obj2 = new ClassC(new ClassB());
        
        obj1.test();
        obj2.test();
    }
}

Output of above program is:

Code
doSomething implementation of A
doSomething implementation of B

This flexibility in method invocation is not available in inheritance and boosts the best practice to favor composition over inheritance.

Unit testing is easy in composition because we know what all methods we are using from superclass and we can mock it up for testing whereas in inheritance we depend heavily on superclass and don’t know what all methods of superclass will be used, so we need to test all the methods of superclass, that is an extra work and we need to do it unnecessarily because of inheritance.

That’s all for multiple inheritances in java and a brief look at composition.

Jetzt 200 € Guthaben sichern

Testen Sie Ihr Setup auf ccloud³

Registrieren Sie sich in der ccloud³ und erhalten Sie 200 € Startguthaben für Ihr Projekt – z. B. für eine PostgreSQL-VM mit automatischen Backups.

centron Redaktion Technische Redaktion

Das Redaktionsteam von centron schreibt Anleitungen aus dem Betriebsalltag: getestet auf unserer eigenen Plattform, betrieben im Rechenzentrum in Hallstadt bei Bamberg.

Kategorie JavaScript
Teilen
Noch offene Fragen?

Our team will help you with your specific setup - in German or English, by people who run the platform themselves.

War dieses Tutorial hilfreich?

Your answer is stored anonymously and helps us improve our tutorials.

Kommentare

No comments yet - be the first to ask a question about this tutorial.

Sign in to comment

Comments are open to centron customers. Sign in to your account to ask a question about this tutorial.

Weiterlesen

Das könnte Sie auch interessieren

Jetzt kostenlos anfangen

Melden Sie sich an und erhalten Sie in den ersten 60 Tagen ein Guthaben von 200 € bei centron.

Dieses Werbeangebot gilt nur für neue Konten. Angebot ausschließlich für Gewerbetreibende.

Jetzt loslegen Sales kontaktieren