What are the differences between abstract classes and interfaces in Java 8?

JavaInterfaceAbstract ClassJava 8Diamond Problem

Java Problem Overview


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?

Java Solutions


Solution 1 - Java

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.

Solution 2 - Java

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.

Solution 3 - Java

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:

interface Bar {
    default int test() { return 42; }
}
interface Baz {
    default int test() { return 6 * 9; }
}
class Foo implements Bar, Baz { }

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:

  • is D a B and a C (i.e. one type of A), or
  • is D a B or a C (i.e. two types of A).

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.

Solution 4 - Java

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.

Solution 5 - Java

> Java 8 though introduces default implementations for interfaces, meaning this is no longer the critical difference between an interface and an abstract class.

Still there are few more critical differences. Refer to this post:

https://stackoverflow.com/questions/19998454/interface-with-default-methods-vs-abstract-class-in-java-8/37971794#37971794

> How do the new Java 8 interfaces avoid the diamond Problem?

Case 1: You are implementing two interfaces, which have same default method, you have to resolve the conflict in your implementation class

interface interfaceA{
	default public void foo(){
		System.out.println("InterfaceA foo");
	}
}
interface interfaceB{
	default public void foo(){
		System.out.println("InterfaceB foo");
	}
}
public class DiamondExample implements interfaceA,interfaceB{
	public void foo(){
		interfaceA.super.foo();
	}
	public static void main(String args[]){
		new DiamondExample().foo();
	}
} 

Above example produce below outout:

InterfaceA foo

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.

interface interfaceA{
	default public void foo(){
		System.out.println("InterfaceA foo");
	}
}

class DiamondBase {
	public void foo(){
		System.out.println("Diamond base foo");
	}
}

public class DiamondExample extends DiamondBase implements interfaceA{
	
	public static void main(String args[]){
		new DiamondExample().foo();
	}
}

Above example produce below output:

Diamond base foo

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionDavid says Reinstate MonicaView Question on Stackoverflow
Solution 1 - JavaskiwiView Answer on Stackoverflow
Solution 2 - JavaMarco13View Answer on Stackoverflow
Solution 3 - JavanosidView Answer on Stackoverflow
Solution 4 - JavaPhilippView Answer on Stackoverflow
Solution 5 - JavaRavindra babuView Answer on Stackoverflow