Interface With Default Methods vs Abstract Class in Java

Samuel Catalano
The Fresh Writes
Published in
4 min readJul 7, 2023

Introduction: In the world of Java programming, developers have various tools at their disposal to design and implement robust and flexible software solutions. Two important concepts often used in object-oriented programming are interfaces and abstract classes. Both interfaces and abstract classes provide mechanisms for defining common behaviour and establishing contracts between classes. However, there are subtle differences between them, particularly when it comes to default methods. In this article, we will explore the distinctions between interface default methods and abstract classes in Java and understand when to use each approach.

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.

Interface With default Method vs Abstract Class

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.

All reasoning we incorporate within the standard procedure must be in consideration of the other procedures defined by the interface. These procedures will remain unaffected by the state of the 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
}

Within the abstract class mentioned above, we encounter a concrete method named isValid(). This method serves the purpose of verifying the integrity of a CircleClass object by examining its current state. By accessing the object’s state, the isValid() method can evaluate the CircleClass instance by the permissible colour options. As a result of this characteristic, the abstract class method grants us the ability to implement various logical operations tailored to the object’s state.

Let’s create a simple implementation class of CircleClass:

public class ChildCircleClass extends CircleClass {
}

Firstly, we shall generate a new occurrence and verify the hue:

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

In this scenario, it becomes apparent that by providing a legitimate colour to the CircleClass instance and invoking the isValid() function, the internal workings of isValid() allow it to examine the state of the CircleClass object. Consequently, it determines whether the instance holds a valid colour or not.

Now, 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 it is commonly understood, an interface cannot possess a state, thereby preventing the default method from accessing the state.

In this context, we have established the getColor() function to deliver the state details. In the child class, the getColor() function will be overridden to furnish the current state of the instance during 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 colour:

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

As we can see, we are superseding the getColor() function within the offspring class to ensure the default procedure verifies the condition during runtime.

Constructors

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

Syntactical Differences

Additionally, there are a 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.

Conclusion

This article highlights the contrast between an abstract class and an interface featuring a default method. Moreover, we have examined the most suitable choice for our specific situation.

Whenever feasible, it is advisable to opt for an interface containing the default method. This selection enables us to both extend a class and implement an interface concurrently.

Remembering that part of the conclusion contains the personal preferences of the author.

--

--

Samuel Catalano
The Fresh Writes

Samuel is a Software Engineer from Brazil with main interests in Java, Spring Boot, Quarkus, Microservices, Docker, Databases, Kubernetes, and Clean Code