Java Top

Get started with Spring 5 and Spring Boot 2, through the Learn Spring course:

>> CHECK OUT THE COURSE

1. Introduction

After the introduction of default methods in Java interfaces, it seemed that there was no longer any difference between an interface and an abstract class. But, that's not the case — there are some fundamental differences between them.

In this tutorial, we'll take a closer look at both the interface and abstract class to see how they differ.

2. Why Use a Default Method?

The purpose of the default method is to provide external functionality without breaking the existing implementations. The original motivation behind introducing the default method was to provide backward compatibility to the Collection Framework with the new lambda functions.

3. Interface With default Method vs Abstract Class

Let's take a look at the main fundamental differences.

3.1. State

The abstract class can have a state, and its methods can access the implementation's state. Although default methods are allowed in an interface, they can't access the implementation's state.

Any logic we write in the default method should be with respect to other methods of the interface — those methods will be independent of the object's state.

Let's say that we've created an abstract class, CircleClass, which contains a String, color, to represent the state of the CircleClass object:

public abstract class CircleClass {

    private String color;
    private List<String> allowedColors = Arrays.asList("RED", "GREEN", "BLUE");

    public boolean isValid() {
        if (allowedColors.contains(getColor())) {
            return true;
        } else {
            return false;
        }
    }

    //standard getters and setters
}

In the above abstract class, we have a non-abstract method called isValid() to validate a CircleClass object based on its state. The isValid() method can access the state of a CircleClass object and validate the instance of CircleClass based on the allowed colors. Due to this behavior, we can write any logic in the abstract class method based on the object's state.

Let's create a simple implementation class of CircleClass:

public class ChildCircleClass extends CircleClass {
}

Now, let's create an instance and validate the color:

CircleClass redCircle = new ChildCircleClass();
redCircle.setColor("RED");
assertTrue(redCircle.isValid());

Here, we can see that when we put a valid color in the CircleClass object and call the isValid() method, internally, the isValid() method can access the state of the CircleClass object and check if the instance contains a valid color or not.

Let's try to do something similar using an interface with a default method:

public interface CircleInterface {
    List<String> allowedColors = Arrays.asList("RED", "GREEN", "BLUE");

    String getColor();
    
    public default boolean isValid() {
        if (allowedColors.contains(getColor())) {
            return true;
        } else {
            return false;
        }
    }
}

As we know, an interface can't have a state, and therefore, the default method can't access the state.

Here, we've defined the getColor() method to provide the state information. The child class will override the getColor() method to provide the state of the instance at runtime:

public class ChidlCircleInterfaceImpl implements CircleInterface {
    private String color;

    @Override
    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }
}

Let's create an instance and validate the color:

ChidlCircleInterfaceImpl redCircleWithoutState = new ChidlCircleInterfaceImpl();
redCircleWithoutState.setColor("RED");
assertTrue(redCircleWithoutState.isValid());

As we can see here, we're overriding the getColor() method in the child class so that the default method validates the state at runtime.

3.2. Constructors

Abstract classes can have constructors, allowing us to initialize the state upon creation. Interfaces, of course, do not have constructors.

3.3. Syntactical Differences

Additionally, there are few differences regarding syntax. An abstract class can override Object class methods, but an interface can't.

An abstract class can declare instance variables, with all possible access modifiers, and they can be accessed in child classes. An interface can only have public, static, and final variables and can't have any instance variables.

Additionally, an abstract class can declare instances and static blocks, whereas an interface can't have either of these.

Finally, an abstract class can't refer to a lambda expression, while the interface can have a single abstract method that can refer to a lambda expression.

4. Conclusion

This article shows the difference between an abstract class and an interface with a default method. We've also seen which one is best suited based on our scenario.

Whenever possible, we should always choose an interface with the default method because it allows us to extend a class and also implement an interface.

As usual, all the code samples shown in this article are available over on GitHub.

Java bottom

Get started with Spring 5 and Spring Boot 2, through the Learn Spring course:

>> CHECK OUT THE COURSE
Generic footer banner
Comments are closed on this article!