In Java there used to be a subtle but important difference between abstract classes and interfaces: default implementations. Abstract classes could have them, interfaces could not. Java 8 though introduces default implementations for interfaces, meaning this is no longer the critical difference between an interface and an abstract class.
So what is?
As best as I can tell, the only remaining difference (besides perhaps some under the hood efficiency stuff) is that abstract classes follow traditional Java single-inheritance, whereas interfaces can have multiple-inheritance (or multiple-implementation if you will). This leads me to another question -
How do the new Java 8 interfaces avoid the diamond Problem?
Interfaces cannot have state associated with them.
Abstract classes can have state associated with them.
Furthermore, default methods in interfaces need not be implemented. So in this way, it will not break already existing code, as while the interface does receive an update, the implementing class does not need to implement it.
As a result you may get suboptimal code, but if you want to have more optimal code, then your job is to override the default implementation.
And lastly, in case a diamond problem occurs, then the compiler will warn you, and you will need to choose which interface you want to implement.
To show more about the diamond problem, consider the following code:
interface A {
void method();
}
interface B extends A {
@Override
default void method() {
System.out.println("B");
}
}
interface C extends A {
@Override
default void method() {
System.out.println("C");
}
}
interface D extends B, C {
}
Here I get the compiler error on interface D extends B, C
, that:
interface D inherits unrelated defaults for method() form types B and C
The fix is:
interface D extends B, C {
@Override
default void method() {
B.super.method();
}
}
In case I wanted to inherit the method()
from B
.
The same holds for if D
were a class
.
To show even more about the difference between interfaces and abstract classes in Java 8, consider the following Team
:
interface Player {
}
interface Team {
void addPlayer(Player player);
}
You can in theory provide a default implementation of addPlayer
such that you can add players to for example a list of players.
But wait...?
How do I store the list of players?
The answer is that you cannot do that in an interface, even if you have default implementations available.