As many of us know Java does not support inheriting from multiple classes and it does support inheriting from multiple interfaces. The less known fact is, what made Java designers to make this choice. In this article I would like to provide my understanding of the issues encountered and the trade offs taken when multiple inheritance is allowed at class level.
Before trying to understand the problem, we might have to get a clear picture of two key attributes of objects, namely state and behaviour.
In any programming language the state of an object is present in its data. In Java, classes will model data with the help of variables, So every variable during the object’s lifetime may posses different values resulting in different states for the object. There are times when some part of data do not have to change at all, We call these data/variables as final/constant fields. Examples include minimumBalance for an Account class, So to create such variables Java provides final keyword. Also most of the times(not always) every final variable’s value is constant for all objects, so it need not be associated with object anymore. Such final variables are marked static. Example include every ‘Account’ object will have same minimum balance. Counter example for this final-static ramification can be given for classes like Planet, Where gravity on planet earth(object) is fixed but not all planets(objects) will have same gravity. So when we declare a variable as both final and static we are effectively making the state of the variable fixed forever for all objects. This can also be viewed as removing the state of that variable.
Basically, the main difference between a class and interface in Java is the presence of state. Class/Objects will have state associated with them where as interfaces do not. The Java language definition of an interface is made stateless by adhering to below points
– Every variable if ever declared should have a fixed(final) value
– Every variable if ever declared should not change its fixed value with the object of its implementing class and hence static
These points can be better illustrated with an example
balance in the above class is an object variable hence its content can be changed during the life of the object. So at a given instance of time, the object will have different value for balance this implies object has different state associated with it
minimumBalance on the other hand is a class variable(static) and whose value is fixed with final. So at a given instance of time, the object will have fixed state with respect to this variable and making the variable stateless
By default, every variable in an interface is static final, This will make the interface stateless.
In any programming language the behaviour is modelled by code or via methods. Class/Object have behaviour where as an interface does not support behaviour(methods until JDK 8). Behaviour of an object is responsible for making changes to the state of an object. To discuss multiple inheritance we require the knowledge of state and behaviour of the involved players(class/interfaces).
Multiple Inheritance With Interfaces
In this section let’s explore what makes multiple inheritance possible with interfaces from the language design point of view.
This is a trivial example of inheriting from multiple interfaces. If we observe the definition of interface closely, All we are saying is, every Employee should have a getSalary behaviour. But we are not talking about how this is done(just contract no behaviour). Similarly, every Player should have a getScore. In every interface definition, we are only concerned about ‘what’ but not ‘how’. Now lets see, what happens when we extend the hierarchy to a common parent say IMan.
The common parent has getHeight contract. The problem with above example is getHeight comes from both IEmployee and IPlayer. Which getHeight should EmployeePlayer implement? Fortunately, this problem is solved in the language by removing state and behaviour out of interface inheritance equation. So as per language design, Since interfaces does not support behaviour or state, IEmployee and IPlayer only carry contract down the hierarchy and not behaviour. Since behaviour is absent here, It does not create any ambiguity with respect to implementation in EmployeePlayer. In other words, There is no distinction of whose contract is implemented until the contract is properly implemented i.e., a method which return integer height.
So this rule in the language design makes perfect sense for behaviour/state less contract coming from any number of parents. The key here is multiple inheritance without behaviour/state is always harmless
Multiple Inheritance With Classes
By definition, classes do carry state/behaviour with them. So now, consider the below scenario where IEmployee and IPlayer are replaced with their concrete implementations Employee and Player respectively. And lets pretend to inherit from Employee and Player.
Currently, Java does not allow you to do this because EmployeePlayer’s parents may have a common contract(method signature) but with different behaviour(method body). Also, we never know from where Employee and Player are derived. This becomes a trivial problem if they are indeed derived from a single common parent as shown below and implementing or overriding their parent’s methods.
Since classes by definition carry state/behaviour in the hierarchy chain, getHeight is no more a contract. It is carried down as behaviour by parent classes Employee and Player to EmployeePlayer. This is an ambiguous situation because the behaviour of getHeight is coming from two places. We cannot unambiguously decide whose behaviour to choose. Most other programming languages do provide some heuristic rules to handle this scenario, But Java on the other hand decided not to allow it altogether as it will complicate the semantics of inheritance hierarchies in the language. This problem has a creative name called the Diamond problem.
Java 8 interface default methods
Java 8 relaxes the language definition of an interface a bit by allowing behaviour on interfaces. Though it does not allow any state to a interface. So for example,
Since default methods are allowed only on interfaces, the behaviour will not change the state of the intermediate interfaces in the inheritance hierarchy. This rule will preserve the contract(interface data) carried down to the concrete implementations without any modifications. When a class tries to implement both IEmployee and IPlayer interfaces, getHeight may come from both the interfaces. This would again lead to the diamond problem. So the language mandates the EmployeePlayer class to override the behaviour else, its a compile time error and this implementation can inturn call one of its parent interface’s default behaviour(method) using parentinterfacename.super.defaultmethod. In a simple sense, default methods on interface works as read only methods for interface defined data.
The similar relaxation to allow multiple inheritance of classes will not be possible because multiple inheritance of classes posses new set of problems because the intermediate classes in the hierarchy will change the state of the classes.
I hope this article shed some light on what makes Java to support multiple inheritance only at interfaces