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?
The definition of the diamond problem is a vague. There are all kinds of issues that can occur with multiple inheritance. Fortunately, most of them can be easily detected at compile-time, and the programming languages support simple solutions to work around these issues. Most of these problems are not even specific to the diamond problem. For example, conflicting definitions of methods can also occur without diamonds:
The specific problem with diamonds is the question of inclusive vs. exclusive. If you have a type hierarchy where B and C derive from A, and D derives from B and C, then the question is:
Well, in Java 8 the type A has to be an interface. So it makes no difference, because interfaces have no state. It doesn't matter, that interfaces can define default methods, since they also have no state. They can invoke methods that have direct access to state. However, these methods are always implemented based on single inheritance.
Now that interfaces can contain executable code, lots of use-cases for abstract classes are taken over by interfaces. But abstract classes can still have member variables, while interfaces can't.
The diamond problem is avoided by simply not allowing a class to implement two interfaces when both interfaces provide a default implementation for the same method with the same signature.
Still there are few more critical differences. Refer to this post:
Interface with default methods vs Abstract class in Java 8
Case 1: You are implementing two interfaces, which have same
default
method, you have to resolve the conflict in your implementation classAbove example produce below outout:
Case 2: You are extending a base class and implementing an interface with default method. Compiler resolves the diamond problem for you and you don't have to resolve it as in first example.
Above example produce below output:
There have been some very detailed answers, but they seem to be missing one point that I at least consider as one of the very few justifications to have abstract classes at all:
Abstract classes can have protected members (and members with default visibility). Methods in interfaces are implicitly public.
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:
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:
In case I wanted to inherit the
method()
fromB
.The same holds for if
D
were aclass
.To show even more about the difference between interfaces and abstract classes in Java 8, consider the following
Team
: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.