JavaRush /Java Blog /Random EN /Multiple inheritance in Java. Composition versus Inherita...
DSergey_Kh
Level 12

Multiple inheritance in Java. Composition versus Inheritance

Published in the Random EN group

Multiple Inheritance in Java

Multiple inheritance makes it possible 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 a diamond problem. And instead of looking for ways to solve this problem, there are better options for how we can achieve the same result as multiple inheritance.

diamond problem

For easier understanding of the diamond problem, 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 - 1Suppose 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 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 doesn't know which superclass method to execute. This is a diamond shaped class diagram called the diamond problem. This is the main reason Java does not support multiple inheritance. Note that the aforementioned problem with multiple class inheritance can only happen to three classes that have at least one common method.

Multiple Inheritance of Interfaces

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 inheritance of interfaces.
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();
	}
}
Note that every time you override any superclass method or implement an interface method, use the @Override. What if we want to use a function methodA()of a class ClassAand a function methodB()of a class ClassBwithin a class ClassC? The solution lies in using composition. Below is a version of the class ClassCthat uses composition to define both class methods and a method doSomething()on 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 practices in Java programming is "approving composition before inheritance". We will explore some of the aspects favoring this approach.
  1. Suppose 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 ClassClike 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 name to remove the compilation error. Also, we will have to change the subclass method in all places where it is called. So inheritance makes our code brittle.

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

  2. Another problem with inheritance is that we expose all methods of the superclass to the client, and if our superclass is not properly designed and there are security gaps, then even though we are doing our best to implement our class, we are affected by poor superclass implementation. . Composition helps us in providing controlled access to superclass methods while 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 provides flexibility in calling methods. Our implementation of the class ClassCabove is suboptimal and provides a compile-time binding 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 output of the program above is:

    doSomething implementation of A
    doSomething implementation of B

    This flexibility in method invocation is not available through inheritance, which adds another benefit to composition choice.

  4. Unit testing is easier to do with composition because we know that all the methods we use are from the superclass and we can copy them for testing. 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, composition is recommended.

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