JavaRush /Java Blog /Random EN /Multiple inheritance in Java. Composition vs. Inheritance...
DSergey_Kh
Level 12

Multiple inheritance in Java. Composition vs. Inheritance

Published in the Random EN group

Multiple Inheritance in Java

Multiple inheritance allows you to create a class that inherits from multiple superclasses. Unlike some other popular object-oriented programming languages, such as C++, Java does not allow multiple inheritance from classes. Java does not support multiple class inheritance because it can lead to the diamond problem. And instead of looking for ways to solve this problem, there are better options how we can achieve the same result like multiple inheritance.

Diamond problem

To understand the diamond problem more easily, let's assume that multiple inheritance is supported in Java. In this case, we could have a class hierarchy as shown in the image below. Multiple inheritance in Java.  Composition vs. Inheritance - 1Let's assume that the class SuperClassis abstract and some method is declared in it. Both concrete classes ClassAand ClassB.
package com.journaldev.inheritance;
public abstract class SuperClass {
	public abstract void doSomething();
}
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(){
	}
}
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 suppose we want to implement ClassCand inherit it from ClassAand ClassB.
package com.journaldev.inheritance;
public class ClassC extends ClassA, ClassB{
	public void test(){
		//calling super class method
		doSomething();
	}
}
Note that the method test()calls the superclass method doSomething(). This leads to ambiguity because the compiler does not know which superclass method to execute. This is a diamond shaped class diagram called a diamond problem. This is the main reason why Java does not support multiple inheritance. Note that the above problem with multiple class inheritance can only happen with three classes that have at least one common method.

Multiple Interface Inheritance

In Java, multiple inheritance is not supported in classes, but it is supported in interfaces. And one interface can extend many other interfaces. Below is a simple example.
package com.journaldev.inheritance;
public interface InterfaceA {
	public void doSomething();
}
package com.journaldev.inheritance;
public interface InterfaceB {
	public void doSomething();
}
Note that both interfaces declare the same method. Now we can create an interface that extends both of these interfaces, as shown in the example below.
package com.journaldev.inheritance;
public interface InterfaceC extends InterfaceA, InterfaceB {
	//same method is declared in InterfaceA and InterfaceB both
	public void doSomething();
}
This works great because interfaces only declare methods and the implementation will be done in classes that inherit the interface. Thus, there is no way to get ambiguity in multiple interface inheritance.
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();
	}
}
Please note that whenever you override any superclass method or implement an interface method, use the annotation @Override. What if we want to use a function methodA()of a class ClassAand a function methodB()of a class ClassBin a class ClassC? The solution lies in the use of composition. Below is a version of the class ClassCthat uses composition to define both the class methods and the method doSomething()of one of the objects.
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 Java programming practices is to “approval composition before inheritance.” We will explore some of the aspects that favor this approach.
  1. Let's say we have a superclass and a class that extends it:

    package com.journaldev.inheritance;
    public class ClassC{
    	public void methodC(){
    	}
    }
    package com.journaldev.inheritance;
    public class ClassD extends ClassC{
    	public int test(){
    		return 0;
    	}
    }

    The above code compiles and works well. But, what if we change the implementation of the class ClassCas shown below:

    package com.journaldev.inheritance;
    public class ClassC{
    	public void methodC(){
    	}
    	public void test(){
    	}
    }

    Note that the method test()already exists in the subclass, but the return type is different. Now the class ClassDwill not compile and if you use any IDE, it will prompt you to change the return type in the superclass or subclass.

    Now imagine a situation where we have a multi-level class inheritance hierarchy and do not have access to the superclass. We will have no choice but to change our subclass method signature or its name to remove the compilation error. We will also have to change the subclass method in all places where it is called. Thus, inheritance makes our code brittle.

    The above problem will never happen with composition and this makes it more attractive to inheritance.

  2. Another problem with inheritance is that we expose all the methods of the superclass to the client and if our superclass is not properly designed and there are security holes, then even though we implement the best implementation of our class, we are affected by the poor implementation of the superclass . Composition helps us in providing controlled access to superclass methods, whereas inheritance does not provide control over superclass methods. This is also one of the main benefits of composition from inheritance.

  3. Another benefit of composition is that it allows for flexibility in calling methods. Our implementation of the class ClassCpresented above is not optimal and ensures that the compile time is tied to the method that will be called. With minimal changes we can make the method call flexible and dynamic.

    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();
    	}
    }

    The result of the program presented above:

    doSomething implementation of A
    doSomething implementation of B

    This flexibility in method calling is not available with inheritance, which adds another benefit to the composition choice.

  4. Unit testing is easier to do with composition because we know that we are using all the methods from the superclass and can copy them for the test. Whereas in inheritance we depend more on the superclass and do not know all the methods of the superclass that will be used. So we have to test all methods of the superclass, which is extra work due to inheritance.

    Ideally, we should only use inheritance when the subclass to superclass relationship is defined as "is". In all other cases, it is recommended to use the composition.

Comments
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION