Multiple Inheritance Java Example
1. Introduction
Multiple inheritance means that a class inherits fields and methods from more than one parent. Java class does not support multiple inheritance as it cannot have more than one parent. Java supports multiple inheritance via interface because an interface can have more than one parent.
You can also check the Java Inheritance Tutorial in the following video:
The “diamond problem” is an ambiguity that arises when two classes B and C inherit from A, and class D inherits from both B and C. If there is a method in A that B and C have overridden, and D does not override it, then which version of the method does D inherit? There is no diamond problem in Java before it introduced default methods in Java 8.
In this example, I will create six interfaces and eight classes as the diagram showing here.
I will use them to demonstrate:
- “Diamond problem” in
DiamondProblemI
–DiamondProblemI
is extended fromInterface_B
andInterface_C
which both extend fromInterface_A
and override thedefaultFoo
method.DiamondProblemI
must override thedefaultFoo
method to void the diamond problem. - “Diamond problem” in
DiamondProblemC
–DiamondProblemC
implementsInterface_B
andInterface_C
which both override the samedefaultFoo
method. This caused a diamond problem too. We fix it by overriding thedefaultFoo
method. - A
ChildClass
extends fromParent_Y
which extends fromBaseClass
. - A
MultiInheritanceI
extends fromInterface_B
andInterface_X
. - A
CompositonClass
has aBaseClass
.
2. Technologies Used
The example code in this article was built and run using:
- Java 11
- Maven 3.3.9
- Eclipse Oxygen
- Junit 4.12
3. Maven Project
3.1 Dependencies
I will include Junit
in the pom.xml
.
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>jcg.zheng.demo</groupId> <artifactId>java-multi-inheritance-demo</artifactId> <version>0.0.1-SNAPSHOT</version> <build> <sourceDirectory>src</sourceDirectory> <plugins> <plugin> <artifactId>maven-compiler-plugin</artifactId> <version>3.8.0</version> <configuration> <release>11</release> </configuration> </plugin> </plugins> </build> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> </dependencies> </project>
4. Interfaces
In this step, I will create six interfaces to demonstrate multiple inheritance and the diamond problem:
Interface_A
– has a default method –defaultFoo
Interface_B
– extends fromInterface_A
and overridesdefaultFoo
Interface_C
– extends fromInterface_A
and overridesdefaultFoo
DiamondProblemI
– extends fromInterface_B
andInterface_C
. Since both parent interfaces have the same default method –defaultFoo
.DiamondProblemI
must overridedefaultFoo
to avoid the “diamond problem”Interface_X
– has a defaultdefaultFooNA
andfoo1
MultiInheritanceI
– extends fromInterface_B
andInterface_X
4.1 Interface_A
In this step, I will create an Interface_A
which has two methods:
defaultFoo
– default methodfoo1
Interface_A.java
package jcg.zheng.demo.api; public interface Interface_A { default String defaultFoo(String msg) { return "Interface_A.defaultFoo is invoked for " + msg; } String foo1(String message); }
4.2 Interface_B
In this step, I will create an Interface_B
which extends from Interface_A
and with two methods:
defaultFoo
– overrides the inherited default methodfoo2
Interface_B.java
package jcg.zheng.demo.api; public interface Interface_B extends Interface_A { @Override default String defaultFoo(String msg) { return "Interface_B.defaultFoo is invoked for " + msg; } String foo2(String subjectName); }
4.3 Interface_C
In this step, I will create an Interface_C
which extends from Interface_A
and three methods:
defaultFoo
– overrides default methodfoo2
foo3
Interface_C.java
package jcg.zheng.demo.api; public interface Interface_C extends Interface_A { @Override default String defaultFoo(String msg) { return "Interface_C.defaultFoo is invoked for " + msg; } String foo2(String dummyData); String foo3(String dummyData); }
4.4 Interface_X
In this step, I will create an Interface_X which has two methods:
defaultFooNA
– default methodfoo1
Interface_X.java
package jcg.zheng.demo.api; public interface Interface_X { default String defaultFooNA(String msg) { return "Interface_X.defaultFooNA is invoked"; } String foo1(String subjectName); }
4.5 Multiple Inheritance Interface
In this step, I will create an interface which extends from both Interface_X
and Interface_B
. There are no duplicate defaults methods from both interfaces.
MultiInheritanceI.java
package jcg.zheng.demo.api; public interface MultiInheritanceI extends Interface_X, Interface_B { }
4.6 Diamond Problem
In this step, I will create an interface called DiamondProblemI
which extends from Interface_B
and Interface_C
. It caused the diamond problem because both Interface_B
and Interface_C
override the defaultFoo
method. DiamondProblemI
must override defaultFoo
.
DiamondProblemI.java
package jcg.zheng.demo.api; public interface DiamondProblemI extends Interface_B, Interface_C { @Override default String defaultFoo(String msg) { return Interface_B.super.defaultFoo(msg); } }
5. Classes
In this step, I will create eight classes:
BaseClass
– It’s the base class for bothParent_X
andParent_Y
.Parent_X
– It extends fromBaseClass
.Parent_Y
– It extends fromBaseClass
.ChildClass
– It extends fromParent_X
.CompositionClass
– It has a field whose type isBaseClass
.DiamondProblemC
– It’s a class which implements bothInterface_B
andInterface_C
which must override thedefaultFoo
method to fix the diamond problem.DiamondProblem
andMultiInheritanceIClass
implement interface which extends from more than one interface.
5.1 Base Class
In this step, I will create a BaseClass which has two data members and one method:
private String someData
– declares private data member is a good practice.protected String shadowingData
–protected
members are inherited by the child class. It will be hidden if the child class has the same variable name.public String foo1(String msg)
– a method in base class is inherited by all its children. When it is changed, all children classes get changed too.
BaseClass.java
package jcg.zheng.demo.api.impl; public class BaseClass { private String someData; protected String shadowingData; public String getSomeData() { return someData; } public void setSomeData(String data) { this.someData = data; } public String foo1(String message) { return "BaseClass.foo1 " + message; } }
5.2 Parent_X Class
In this step, I will create a Parent_X
class which extends from BaseClass
. It has the name
field and foo2
method.
Parent_X.java
package jcg.zheng.demo.api.impl; public class Parent_X extends BaseClass { private String name; public String foo2(String subjectName) { setName(subjectName); return "Hello, " + subjectName; } public String getName() { return name; } public void setName(String cName) { this.name = cName; } }
5.3 Parent_Y Class
In this step, I will create a Parent_Y
class which extends from BaseClass
. It has the field – shadowingData
which has the same name with a field in BaseClass
. The BaseClass.shadowingData
is inaccessible from Parent_Y.
Parent_Y.java
package jcg.zheng.demo.api.impl; public class Parent_Y extends BaseClass { private String message; protected String shadowingData; public Parent_Y(String message) { super(); this.message = message; } public String foo2(String data) { return message + " foo2: " + data; } public String foo3(String data) { return message + " foo3: " + data; } }
5.4 Child Class
In this step, I will create a ChildClass
which extends from Parent_Y
.
ChildClass.java
package jcg.zheng.demo.api.impl; public class ChildClass extends Parent_Y { public ChildClass(String message) { super(message); } }
5.5 Multiple Inheritance Class
In this step, I will create a MultiInheritanceIClass
which implements MultiInheritanceI
.
MultiInheritanceIClass.java
package jcg.zheng.demo.api.impl; import jcg.zheng.demo.api.MultiInheritanceI; public class MultiInheritanceIClass implements MultiInheritanceI { @Override public String foo1(String subjectName) { return this.getClass() + " foo1"; } @Override public String foo2(String subjectName) { return this.getClass() + " foo2"; } }
5.6 Diamond Problem Class
In this step, I will create a DiamondProblem
which implements DiamondProblemI
.
DiamondProblem.java
package jcg.zheng.demo.api.impl; import jcg.zheng.demo.api.DiamondProblemI; public class DiamondProblem implements DiamondProblemI { @Override public String foo1(String message) { return this.getClass() + " foo1"; } @Override public String foo2(String dummyData) { return this.getClass() + " foo2"; } @Override public String foo3(String dummyData) { return this.getClass() + " foo3"; } }
5.7 Diamond Problem Interface Class
In this step, I will create a DiamondProblemC
which implements Interface_B
and Interface_C
. DiamondProblemC
must override the defaultFoo
method to avoid the diamond problem.
DiamondProblemC.java
package jcg.zheng.demo.api.impl; import jcg.zheng.demo.api.Interface_B; import jcg.zheng.demo.api.Interface_C; public class DiamondProblemC implements Interface_B, Interface_C { @Override public String defaultFoo(String msg) { return Interface_C.super.defaultFoo(msg); } @Override public String foo1(String message) { return this.getClass() + " foo1"; } @Override public String foo2(String subjectName) { return "foo2 " + subjectName; } @Override public String foo3(String data) { return ("foo3 " + data); } }
5.8 CompositionClass
In this step, I will create a CompositionClass
which has a BaseClass
object member and a field whose name matches the same in BaseClass
. But BaseClass.shadowingData
will not be hidden from CompositionClass
. If the BaseClass
does not meet the requirement, it’s easy to switch to a new class without considering the impact to others.
CompositionClass.java
package jcg.zheng.demo.api.impl; public class CompositionClass { //if BaseClass does not meet the requirement, then you can include a different object with the more suitable method private BaseClass bCls; protected String shadowingData; public BaseClass getbCls() { return bCls; } public void setbCls(BaseClass bCls) { this.bCls = bCls; } }
6. JUnit Test
In this step, I will demonstrate the multiple inheritance via Junit tests.
6.1 Child Test
In this step, I create a ChildTest
to demonstrate that a class can inherit from more than one level of inheritance. It also shows that shadowing variable is a bad practice because there is no way to set its value. See line 26 for details.
ChildClassTest.java
package jcg.zheng.demo.api.impl; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import org.junit.Test; public class ChildClassTest { private ChildClass testClass = new ChildClass("Mary"); @Test public void foo2() { assertEquals("Mary foo2: Zheng", testClass.foo2("Zheng")); } @Test public void foo3() { assertEquals("Mary foo3: Zheng", testClass.foo3("Zheng")); } @Test public void shadowingData_is_bad_practice() { testClass.shadowingData = "Test"; assertEquals("Test", ((Parent_Y) testClass).shadowingData); assertNull(((BaseClass) testClass).shadowingData); } }
Junit Output
------------------------------------------------------- T E S T S ------------------------------------------------------- Running jcg.zheng.demo.api.impl.ChildClassTest Tests run: 3, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.145 sec Results : Tests run: 3, Failures: 0, Errors: 0, Skipped: 0
6.2 Parent_X Test
In this step, I will create a test for Parent_X
.
Parent_XTest.java
package jcg.zheng.demo.api.impl; import static org.junit.Assert.assertEquals; import org.junit.Test; public class Parent_XTest { private Parent_X testClass = new Parent_X(); @Test public void foo1() { // if BaseClass.foo1 not meet the sub class's need. then you must be change at // the super class which other subclass may not agree with you! assertEquals("BaseClass.foo1 Test", testClass.foo1("Test")); } @Test public void foo2() { assertEquals("Hello, Zheng", testClass.foo2("Zheng")); } @Test public void test_inherited_method() { testClass.setName("Local data"); testClass.setSomeData("Common data"); assertEquals("Common data", testClass.getSomeData()); assertEquals("Local data", testClass.getName()); } }
Junit Output
------------------------------------------------------- T E S T S ------------------------------------------------------- Running jcg.zheng.demo.api.impl.Parent_XTest Tests run: 3, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.222 sec Results : Tests run: 3, Failures: 0, Errors: 0, Skipped: 0
6.3 Parent_Y Test
In this step, I will create a test for Parent_Y
. Please note that the line 39 – the BaseClass.shadowingData
is inaccessible.
Parent_YTest.java
package jcg.zheng.demo.api.impl; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import org.junit.Test; public class Parent_YTest { private Parent_Y testClass = new Parent_Y("Mary"); @Test public void foo1() { // if BaseClass.foo1 not meet the sub class's need. then you must be change at // the super class which other subclass may not agree with you! assertEquals("BaseClass.foo1 Zheng", testClass.foo1("Zheng")); } @Test public void foo2() { assertEquals("Mary foo2: Zheng", testClass.foo2("Zheng")); } @Test public void foo3() { assertEquals("Mary foo3: Zheng", testClass.foo3("Zheng")); } @Test public void inherited_methods() { testClass.setSomeData("Common Data"); assertEquals("Common Data", testClass.getSomeData()); } @Test public void shadowingData_is_bad_as_it_hide_fields() { testClass.shadowingData = "Test"; assertEquals("Test", testClass.shadowingData); assertNull(((BaseClass) testClass).shadowingData); } }
Junit Output
------------------------------------------------------- T E S T S ------------------------------------------------------- Running jcg.zheng.demo.api.impl.Parent_YTest Tests run: 5, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.171 sec Results : Tests run: 5, Failures: 0, Errors: 0, Skipped: 0
6.4 Multiple Inheritance Test
In this step, I will create a test for MultiInheritanceClass
.
MultiInheritanceIClassTest.java
package jcg.zheng.demo.api.impl; import static org.junit.Assert.assertEquals; import org.junit.Test; public class MultiInheritanceIClassTest { private MultiInheritanceIClass testClass = new MultiInheritanceIClass(); @Test public void defaultFoo() { assertEquals("Interface_B.defaultFoo is invoked for Mary", testClass.defaultFoo("Mary")); } @Test public void defaultFooNA() { assertEquals("Interface_X.defaultFooNA is invoked", testClass.defaultFooNA("Mary")); } @Test public void foo1() { assertEquals("class jcg.zheng.demo.api.impl.MultiInheritanceIClass foo1", testClass.foo1("Test")); } @Test public void foo2() { assertEquals("class jcg.zheng.demo.api.impl.MultiInheritanceIClass foo2", testClass.foo2("Test")); } }
Junit Output
------------------------------------------------------- T E S T S ------------------------------------------------------- Running jcg.zheng.demo.api.impl.MultiInheritanceIClassTest Tests run: 4, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.175 sec Results : Tests run: 4, Failures: 0, Errors: 0, Skipped: 0
6.5 Diamond Problem Test
In this step, I will create a test for DiamondProblem
.
DiamondProblemTest.java
package jcg.zheng.demo.api.impl; import static org.junit.Assert.assertEquals; import org.junit.Test; public class DiamondProblemTest { private DiamondProblem testClass = new DiamondProblem(); @Test public void defaultFoo() { assertEquals("Interface_B.defaultFoo is invoked for Mary", testClass.defaultFoo("Mary")); } @Test public void foo1() { assertEquals("class jcg.zheng.demo.api.impl.DiamondProblem foo1", testClass.foo1("Zheng")); } @Test public void foo2() { assertEquals("class jcg.zheng.demo.api.impl.DiamondProblem foo2", testClass.foo2("Test")); } @Test public void foo3() { assertEquals("class jcg.zheng.demo.api.impl.DiamondProblem foo3", testClass.foo3("Test")); } }
Junit Output
------------------------------------------------------- T E S T S ------------------------------------------------------- Running jcg.zheng.demo.api.impl.DiamondProblemTest Tests run: 4, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.159 sec Results : Tests run: 4, Failures: 0, Errors: 0, Skipped: 0
6.6 Diamond Problem Interface Test
In this step, I will create a test for DiamondProblemC
.
DiamondProblemCTest.java
package jcg.zheng.demo.api.impl; import static org.junit.Assert.assertEquals; import org.junit.Test; public class DiamondProblemCTest { private DiamondProblemC testClass = new DiamondProblemC(); @Test public void defaultFoo() { assertEquals("Interface_C.defaultFoo is invoked for Mary", testClass.defaultFoo("Mary")); } @Test public void foo1() { assertEquals("class jcg.zheng.demo.api.impl.DiamondProblemC foo1", testClass.foo1("Zheng")); } @Test public void foo2() { assertEquals("foo2 Zheng", testClass.foo2("Zheng")); } @Test public void foo3() { assertEquals("foo3 Zheng", testClass.foo3("Zheng")); } }
Junit Output
------------------------------------------------------- T E S T S ------------------------------------------------------- Running jcg.zheng.demo.api.impl.DiamondProblemCTest Tests run: 4, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.213 sec Results : Tests run: 4, Failures: 0, Errors: 0, Skipped: 0
6.7 Composition Test
In this step, I will create a test for CompositionClass
. Please note that line 15 and 19 show that BaseClass.shadowingData
is accessible when it is used as a composition than inheritance.
CommonService.java
package jcg.zheng.demo.api.impl; import static org.junit.Assert.assertEquals; import org.junit.Test; public class CompositionClassTest { private CompositionClass testClass = new CompositionClass(); @Test public void composition_not_shadowingData() { BaseClass bCls = new BaseClass(); bCls.setSomeData("Test"); bCls.shadowingData = "BaseClass.shadowingData is not shadowing at composition"; testClass.setbCls(bCls ); assertEquals("Test", testClass.getbCls().getSomeData()); assertEquals("BaseClass.shadowingData is not shadowing at composition", testClass.getbCls().shadowingData); } }
Junit Output
------------------------------------------------------- T E S T S ------------------------------------------------------- Running jcg.zheng.demo.api.impl.CompositionClassTest Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.117 sec Results : Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
7. Summary
In this example, I demonstrated how Java supports multiple inheritance via interface and explained how the diamond problem is introduced after Java 8 introduced the default method. I also demonstrated that using object with composition has less impact than inheritance.
8. Download the Source Code
This example consists of a Maven project which demonstrates multiple inheritance in Java.
You can download the full source code of this example here: Multiple Inheritance Java Example