Multiple Inheritance in Java
Multiple inheritance is the ability to create classes with multiple parent classes. Unlike other popular object-oriented languages such as C++, Java does not support multiple class inheritance. He does not support it because of the likelihood of encountering the “diamond problem” and instead prefers to provide some kind of comprehensive approach to solve it, using the best options that we can achieve a similar inheritance result."The Diamond Problem"
To understand the diamond problem more simply, let's assume that multiple inheritance is supported in Java. In this case, we can get classes with the hierarchy shown in the figure below. Let's assume thatSuperClass
is an abstract class that describes a certain method, and the classes
ClassA
and
ClassB
are real classes.
SuperClass.java
package com.journaldev.inheritance;
public abstract class SuperClass {
public abstract void doSomething();
}
ClassA.java
package com.journaldev.inheritance;
public class ClassA extends SuperClass{
@Override
public void doSomething(){
System.out.println("Какая-то реализация класса A");
}
//собственный метод класса ClassA
public void methodA(){
}
}
Now, let's assume that the class
ClassC
inherits from
ClassA
and
ClassB
at the same time, and at the same time has the following implementation:
package com.journaldev.inheritance;
public class ClassC extends ClassA, ClassB{
public void test(){
//вызов метода родительского класса
doSomething();
}
}
Note that the method
test()
calls a method
doSomething()
of the parent class, which will lead to ambiguity because the compiler does not know which superclass method should be called. Due to the shape of the class inheritance diagram in this situation, which resembles the outline of a faceted diamond, the problem is called the “Diamond Problem”. This is the main reason why Java does not support multiple class inheritance. Note that this problem with multiple class inheritance can also occur with three classes that have at least one common method.
Multiple inheritance and interfaces
You may have noticed that I always say "multiple inheritance is not supported between classes", but it is supported between interfaces. A simple example is shown below:InterfaceA.java
package com.journaldev.inheritance;
public interface InterfaceA {
public void doSomething();
}
InterfaceB.java
package com.journaldev.inheritance;
public interface InterfaceB {
public void doSomething();
}
Notice that both interfaces have a method with the same name. Now let's say we have an interface that inherits from both interfaces.
InterfaceC.java
package com.journaldev.inheritance;
public interface InterfaceC extends InterfaceA, InterfaceB {
//метод, с тем же названием описан в InterfaceA и InterfaceB
public void doSomething();
Here, everything is ideal, since interfaces are only a reservation/description of a method, and the implementation of the method itself will be in the concrete class that implements these interfaces, so there is no possibility of encountering ambiguity with multiple inheritance of interfaces. This is why classes in Java can inherit from multiple interfaces. Let's show it with the example below.
InterfacesImpl.java
package com.journaldev.inheritance;
public class InterfacesImpl implements InterfaceA, InterfaceB, InterfaceC {
@Override
public void doSomething() {
System.out.println("doSomething реализация реального класса ");
}
public static void main(String[] args) {
InterfaceA objA = new InterfacesImpl();
InterfaceB objB = new InterfacesImpl();
InterfaceC objC = new InterfacesImpl();
//все вызываемые ниже методы получат одинаковую реализацию конкретного класса
objA.doSomething();
objB.doSomething();
objC.doSomething();
}
}
You may have noticed that every time I override a method described in a superclass or in an interface, I use the @Override annotation. This is one of the three built-in Java annotations and you should always use it when overriding methods.
Composition as salvation
So what if we want to use amethodA()
class
ClassA
and
methodB()
class function
ClassB
in
ClassС
? A solution to this could be composition - a rewritten version
ClassC
that implements both class methods
ClassA
and
ClassB
also has an implementation
doSomething()
for one of the objects.
ClassC.java
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 or inheritance?
It is good Java programming practice to take advantage of composition over inheritance. We will look at some aspects in favor of this approach.-
Suppose we have the following combination of parent-heir classes:
ClassC.java
package com.journaldev.inheritance; public class ClassC{ public void methodC(){ } }
ClassD.java
package com.journaldev.inheritance; public class ClassD extends ClassC{ public int test(){ return 0; } }
The code above compiles and works fine, but what if
ClassC
it were implemented differently:package com.journaldev.inheritance; public class ClassC{ public void methodC(){ } public void test(){ } }
Note that the method
test()
already exists in the descendant class, but returns a result of a different type. NowClassD
, in case you are using an IDE, it will not compile. You will be advised to change the return type in the descendant or superclass.Now let's imagine a situation where there is multi-level inheritance of classes and the superclass is not available for our changes. Now, to get rid of the compilation error, we have no other options than to change the signature or name of the subclass method. We will also have to make changes to all places where this method was called. Thus, inheritance makes our code brittle.
The problem described above never occurs in the case of composition, and therefore makes the latter preferable to inheritance.
-
The next problem with inheritance is that we expose all the methods of the parent to the client. And if the superclass is not designed very correctly and contains security holes. Then, even though we take full care of security in the implementation of our subclass, we will still depend on the flawed implementation of the parent class.
Composition helps us in providing controlled access to the methods of a superclass, whereas inheritance does not maintain any control over its methods. This is also one of the main advantages of composition over inheritance.
-
Another benefit of composition is that it adds flexibility when calling methods. The implementation of the class
ClassC
described above is not optimal and uses early binding to the called method. Minimal changes will allow us to make method calling flexible and allow late binding (binding at runtime).ClassC.java
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 program above will display:
doSomething implementation of A doSomething implementation of B
This flexibility in method calling is not seen with inheritance, which makes composition the best approach.
-
Unit testing is easier in the case of composition because we know that for all the methods that are used in the superclass we can stub the tests, while in inheritance we depend heavily on the superclass and don't know how the methods of the parent class will be used. So, due to inheritance, we will have to test all the methods of the superclass, which is unnecessary work.
Ideally, inheritance should only be used when the “ is-a ” relationship is true for the parent and child classes, otherwise composition should be preferred.
GO TO FULL VERSION