Multiple Inheritance in Java

 

Joseph Bergin
Pace University
jbergin@pace.edu
http://csis.pace.edu/~bergin

Introduction

Java was designed without multiple inheritance. While some developers think of this as a flaw, it is actually true that the overall design of Java supports the solution of problems commonly solved with multiple inheritance in other ways. In particular, the singly rooted hierarchy (with Object as the ultimate ancestor of all classes) and Java interfaces solves most problems that are commonly solved using multiple inheritance in C++.

However, there are a few situations in which multiple inheritance is very helpful. In this note we will consider one special case and also the general case of multiple inheritance.

Mixin Inheritance

In mixin inheritance, one class is specifically designed to be used as one of the classes in a multiple inheritance scheme. We say that it provides some functionality that is "mixed in" to some other class that wants this functionality. Another way to think of mixin inheritance is that a mixin class is given a new parent class so that the mixin seems to extend the other class. In some projects it is necessary to rely on common services that must be provided by several classes. Mixin inheritance is one way to centralize the development of these services.

To provide for mixin inheritance we will need to define two interfaces as well a at least one class that provides the service: the Mixin class. In some situations, one of these interfaces is empty and may then be omitted.

The Interfaces

The first interface (always required) defines what the mixin class will provide for services. It defines one or more methods that will be implemented by the mixin class. We will take a simple and abstract example here. The class will be called (abstractly) MProvides to emphasize that it defines what any compatible mixin must provide. We will also assume that the only service provided is a void function and we will give it the abstract name func. In practice, however, there may be any number of methods defined and they may have any signatures.

interface MProvides
{	void func();
}

One special feature of mixin inheritance that is not usually present in the general multiple inheritance case is that the mixin class may require services of the class into which it is mixed. That is, in order to provide the "func" service, the mixin may need to get some information from the other class. We define this with another interface. We will give it the abstract name MRequires to indicate that it requires one or more services from the class into which it is mixed.

Here we will suppose that the compatible mixins require that the other class provides a method getValue that returns an int.

interface MRequires
{	int getValue();
}

In general, MRequires will have a more appropriate name and will have one or more methods of arbitrary signature. However, any class into which we mix our mixin must provide services with the given names and signatures, though it need not explicitly implement the MRequires interface. If MRequires is empty it may be omitted.

The Mixin

The mixin class itself will implement the MProvides interface. It will also be created by passing its constructor an argument that implements MRequires. Here is a simple example called (again abstractly) Mixin.

class Mixin implements MProvides
{	public Mixin(MRequires parent) { this.parent = parent; }

	public void func() { System.out.println("The value is: " + parent.getValue()); }

	private final MRequires parent;
}

When a new Mixin is created it knows about an MRequires object. It can then query this object using the services defined in MRequires in order to provide its own services. We have called this object parent to emphasize that the intent is to simulate giving the Mixin class a new parent class. If MRequires is empty, no object need be passed into the constructor. Note that in general, the Mixin constructor may require other parameters as well.

The Class Used as the Base

Now suppose that we wish to mix this class into another class. This class must have all of the methods required of the MRequires interface, but it need not implement this interface. It will probably have other methods as well. Here is an example.

Class Parent
{	public P(int value ) { this.val = value; }
	public int getValue() { return this.val; }
	
	public toString() { return "" + this.val; }
	private int val;
}

The Result of Mixing

Now, to actually mix the two classes together we first build a new class that extends Parent and implements both of our interfaces

class Child extends Parent implements MRequires, MProvides
{

This class defines both the services of Parent and those of the mixin (MProvides). To implement the Child class we create a new Mixin object and save it. We will also delegate all messages defined in the MProvides interface to this object. When we create the Mixin object we need to pass it an object that implements MRequires, but this object does so as this new class implements the MRequires interface.

	public Child(int value)
	{	super(value); 
		this.mixin = new Mixin(this);
	}

	public void func(){ mixin.func(); }

	private final MProvides mixin;

}

Note that the service named func is provided by the Mixin object as previously defined. It does not need to be redefined in the Child class. Also note that the Child class automatically implements MRequires since the inherited method fulfills the necessary contract defined by the MRequires interface. We do have to provide the simple delegation function for the method(s) of MProvides, however.

The key here is that we were able to design the Mixin class to be a mixin and so define the two required interfaces. Note that it is actually the services defined by MProvides that are mixed in, not, properly speaking, the Mixin class itself. This actually adds some flexibility since several classes might implement this interface and so be mixed in to other classes to provide this form of multiple inheritance.

General Multiple Inheritance

We can use the above to see how to provide general multiple inheritance. The difference here is that we may not be able to design one of the classes to be a mixin. In the usual case, the two classes are predefined and do not require services from each other. This means that the MRequires interface is not needed. However, we still need to define one interface, since Java won't let us mix two classes together.

If we do have the luxury of designing at least one of the classes then we can proceed as before, treating that class as the mixin and defining an interface that it will implement. Otherwise we need to do a bit more. Suppose that we would like to mix the class Parent of the previous section with a class Other defined below.

Class Other
{	public Other(int value) { ... }
	
	public void whatever()
	{...
	}
}

Since Java will let us extend only one class, we need to define an interface that declares the public features of the other. We will do this to make our life simple here, though you may choose to do the next step for the class of least importance and whose methods are going to be called least often. Here we will define an interface to give the public methods of the class Other.

Interface OtherInterface
{	void whatever();
}

We can now build a subclass of Other by doing nothing more than implementing this new interface and providing any required constructors (which are not inherited).

class OtherChild extends Other implements OtherInterface
{	public OtherChild (int value){ super(value); }
}

If we had the freedom to modify class Other we could avoid the class OtherChild and just have Other implement this new interface.

This new class is just like an Other, but it advertises that it implements the OtherInterface. From here we can proceed as in the mixin case by extending the Parent class, implementing he OtherInterface and creating a new OtherChild object to which we delegate the messages defined in the OtherInterface.

Class ParentChild extends Parent implements OtherInterface
{	public ParentChild(...) { child = new OtherChild(...); ... }

	public void whatever() { child.whatever(); }

	private final OtherInterface child;
}

So, in this class we have merged the actual implementations of two other classes, Parent and Other without modifying either class. This is general multiple inheritance. In Java we needed to define and implement interfaces and use delegation to an object of one of the classes to achieve this.

Conclusion

Multiple inheritance is needed only rarely. The fact that it is a bit awkward to achieve in Java is not a disadvantage as it may discourage you from using it in those cases in which a better solution is available. However, you should be aware that extensions to Java are not necessary to achieve the use of most features that Java seems to lack. Rather the Java was a good, sound, and complete language design that supports the kinds of things that developers need. It takes skill, however, to see how to achieve some of the less used idioms such as multiple inheritance.


Last Updated: June 26, 2000