How does Java 8' new default interface model works (incl. diamond, multiple inheritance, and precedence)?
Here is a detailed explanation for Java 8' new interface model & the diamond problem of multiple inheritance.
As you might see in this examples, starting with JDK 8, Java
has introduced a kind of multiple inheritance as both, the
class and its interface might contain an
implementation of the same method (same name
& signature). To address the diamond
problem there is a precedence in which order an
implementation is used: only if the class implements all default / optional methods of its
interfaces, the code can be compiled and the implementations of this
class are used. Otherwise the compiler tries to patch the
missing implementation(s) with interface's default
implementation. And if there are multiple default implementations
of a method, then the diamond
problem occurs and the compiler rejects the compilation.
Java 8' new interfaces model is the result of
approaching backwards compatibility, i. e. to keep
existing code that was written against pre Java 8 interfaces
compilable.
Multiple Inheritance Ambiguity with Interface
The diamond problem only applies to implementation inheritance (extends
in all versions of Java prior to Java 8). It doesn't apply to API inheritance (implements
in all versions of Java prior to Java 8).
Since interface methods with matching type signatures are compatible, there is no diamond problem if you inherit the same method signature twice: matching method signatures simply coalesce instead. (And if the type signatures aren't the same, then you don't have the diamond problem either.)
In Java 7 and below, the only way to inherit implementation code was via the extends
keyword, which restricts to at most one parent. Therefore there is no multiple implementation inheritance and the diamond problem does not exist.
Java 8 adds a new wrinkle because it allows interfaces to have implementation code. It still escapes the diamond problem by simply falling back to the previous behavior (no implementation inheritance) when you're implementing multiple interfaces with methods that have matching signatures.
Doesn't Default in Java 8 destroy the whole idea of not allowing us to do multiple inheritance?
Can someone help me understand why they would add Default in Java 8 to
allow implementation to interfaces instead of just allowing us to have
multiple inheritance in abstract classes?
One important reason is super class state. For example;
class OpticalDrive {
}
class DVDDrive extends OpticalDrive {
protected int speed = 5;
}
class CDDrive extends OpticalDrive {
protected int speed = 10;
}
class ComboDrive extends DVDDrive, CDDrive {
ComboDrive() {
System.out.println(super.speed);
super.speed = 15;
}
}
In above hypothetical example speed
state variable is multiple inherited and reading or modifying it creates ambiguity as to which one must be modified.
What exactly is the difference between just allowing us to extend
multiple classes verses allowing us to create Default methods in
interfaces?
Since interfaces can't have mutable state variables it is better to have default methods in interfaces rather than enabling multiple inheritance of abstract classes. Following example shows Java 8 default method in interface in action.
public interface DvdReader {
default public void read() {
System.out.println("DvdReader.read()");
}
}
public interface CdReader {
default public void read() {
System.out.println("CdReader.read()");
}
}
public class ComboReader implements CdReader, DvdReader {
public void read() {
CdReader.super.read();
DvdReader.super.read();
}
public static void main(String[] args) {
new ComboReader().read();
}
}
In the above scenario if you ignore overriding read
method in ComboReader
, the compiler will complain with the following error.
ComboReader.java:1: error: class ComboReader inherits unrelated defaults for read() from types CdReader and DvdReader
public class ComboReader implements CdReader, DvdReader {
^
1 error
Furthermore enabling multiple abstract class inheritance means the syntax of Java code has to change significantly causing java's ability to backward compatibility.
What happens, if two interfaces contain the same default method?
You cannot implement multiple interfaces having same signature of
Java 8 default methods (without overriding explicitly in child class)
. You can solve it by implementing the method E.g.
class MyClass implements alpha, beta {
void display() {
System.out.println("This is not default");
}
@Override
public void reset() {
//in order to call alpha's reset
alpha.super.reset();
//if you want to call beta's reset
beta.super.reset();
}
}
Why does Java not allow multiple inheritance but does allow conforming to multiple interfaces with default implementations
Things are not so simple.
If a class implements multiple interfaces that defines default methods with the same signature the compiler will force you to override this method for the class.
For example with these two interfaces :
public interface Foo {
default void doThat() {
// ...
}
}
public interface Bar {
default void doThat() {
// ...
}
}
It will not compile :
public class FooBar implements Foo, Bar{
}
You should define/override the method to remove the ambiguity.
You could for example delegate to the Bar
implementation such as :
public class FooBar implements Foo, Bar{
@Override
public void doThat() {
Bar.super.doThat();
}
}
or delegate to the Foo
implementation such as : :
public class FooBar implements Foo, Bar {
@Override
public void doThat() {
Foo.super.doThat();
}
}
or still define another behavior :
public class FooBar implements Foo, Bar {
@Override
public void doThat() {
// ...
}
}
That constraint shows that Java doesn't allow multiple inheritancy even for interface default methods.
I think that we cannot apply the same logic for multiple inheritances because multiples issues could occur which the main are :
- overriding/removing the ambiguity for a method in both inherited classes could introduce side effects and change the overall behavior of the inherited classes if they rely on this method internally. With default interfaces this risk is also around but it should be much less rare since default methods are not designed to introduce complex processings such as multiple internal invocations inside the class or to be stateful (indeed interfaces cannot host instance field).
- how to inherit multiple fields ? And even if the language allowed it you would have exactly the same issue as this previously quoted : side effect in the behavior of the inherited class : a
int foo
field defined in aA
andB
class that you want to subclass doesn't have the same meaning and intention.
Multiple Inheritance :Java vs C++
Java (unlike C++) does not allow multiple inheritance of state and, therefore, does not suffer from a diamond problem.
It allows multiple inheritance of type through interfaces (a class can implement multiple interfaces).
Starting with Java 8 there is also multiple inheritance of behavior through default
methods in interfaces.
Why interface methods have no body
Because Java, in contrast to languages like C++ or Eiffel, only has multiple inheritance of types (i.e. interfaces as well as one class), not multiple inheritance of state and behaviour. The latter of which add enormous complexity (especially state).
The Java designers (and C#, for that matter) opted to not include it as it presented C++ programmers often with very hard to debug issues. You can solve pretty much most problems that require true multiple inheritance with implementing multiple interfaces, so the tradeoff was deemed worth it.
Note that multiple inheritance of behaviour (not state) might come to Java 8 (unless they postpone it again like one of the many other things) in form of virtual extension methods where an interface can declare a method that delegates to one in another class, which then exists on all types that implement that interface.
Related Topics
How to Convert a Byte Array to Its Numeric Value (Java)
How to Tell Jackson to Ignore a Property for Which I Don't Have Control Over the Source Code
Java: Get Current Date and Time from Server Not System Clock
Uses for the Java Void Reference Type
How Does Java's Preparedstatement Work
Difference Between Shutdown and Shutdownnow of Executor Service
Java Jdbc Access Denied for User
Checked VS Unchecked Exception
Composing Swing Components: How to Add the Ability to Add Actionlisteners
Calling a @Bean Annotated Method in Spring Java Configuration
Registering and Using a Custom Java.Net.Url Protocol
Java 'File.Delete()' Is Not Deleting Specified File
Are There Best Practices for (Java) Package Organization
How to Convert a Java Object (Bean) to Key-Value Pairs (And Vice Versa)
Simple Way to Count Character Occurrences in a String
How to Demonstrate Java Multithreading Visibility Problems
What Are Good Installanywhere Replacements for Installing a Java Ee Application