Core Java

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:

Java Inheritance Tutorial – 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.

Multiple Inheritance Java - Class Diagram
Figure 1 Class Diagram

I will use them to demonstrate:

  • “Diamond problem” in DiamondProblemIDiamondProblemI is extended from Interface_B and Interface_C which both extend from Interface_A and override the defaultFoo method. DiamondProblemI must override the defaultFoo method to void the diamond problem.
  • “Diamond problem” in DiamondProblemCDiamondProblemC implements Interface_B and Interface_C which both override the same defaultFoo method. This caused a diamond problem too. We fix it by overriding the defaultFoo method.
  • A ChildClass extends from Parent_Y which extends from BaseClass.
  • A MultiInheritanceI extends from Interface_B and Interface_X.
  • A CompositonClass has a BaseClass.

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 from Interface_A and overrides defaultFoo
  • Interface_C – extends from Interface_A and overrides defaultFoo
  • DiamondProblemI – extends from Interface_B and Interface_C. Since both parent interfaces have the same default method – defaultFoo. DiamondProblemI must override defaultFoo to avoid the “diamond problem”
  • Interface_X – has a default defaultFooNA and foo1
  • MultiInheritanceI – extends from Interface_B and Interface_X

4.1 Interface_A

In this step, I will create an Interface_A which has two methods:

  • defaultFoo – default method
  • foo1

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 method
  • foo2

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 method
  • foo2
  • 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 method
  • foo1

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 both Parent_X and Parent_Y.
  • Parent_X – It extends from BaseClass.
  • Parent_Y – It extends from BaseClass.
  • ChildClass – It extends from Parent_X.
  • CompositionClass – It has a field whose type is BaseClass.
  • DiamondProblemC – It’s a class which implements both Interface_B and Interface_C which must override the defaultFoo method to fix the diamond problem.
  • DiamondProblem and MultiInheritanceIClass 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 shadowingDataprotected 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.

Download
You can download the full source code of this example here: Multiple Inheritance Java Example

Mary Zheng

Mary has graduated from Mechanical Engineering department at ShangHai JiaoTong University. She also holds a Master degree in Computer Science from Webster University. During her studies she has been involved with a large number of projects ranging from programming and software engineering. She works as a senior Software Engineer in the telecommunications sector where she acts as a leader and works with others to design, implement, and monitor the software solution.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Back to top button