Java: Good reasons to add method implementation to interface

Create cleaner and more robust code by implementing methods defined in your interface.

Osaretin Idele
Level Up Coding

--

Photo by Mike Dorner on Unsplash

The idea behind the concept of interface as defined in Java and other programming languages is to define a contract or behavior that classes must implement or conform to. By defining an interface, a service provider can outline a set of requirements any class should conform to in order to use the service being provided.

In the Java releases prior to Java 8 released in 2014, an interface allows you to define the method signature and constants without any implementation. A class that conforms to a particular interface provides the actual implementation for the declared methods.

To consider a specific example, to be able to use the sort method of the Arrays class to sort an array of objects, all the objects must provide an implementation for the Comparable interface

If you store an object of Vehicle in an array, to use the sort method of the array, you must implement this Comparable interface and provide the actual code implementation to perform the comparison of the Vehicle objects.

Instead of just allowing you to only define allowed method signature, Java 8 and later releases also allow you to provide some code implementation right inside your interface definition. For example, in addition to just defining the compareTo(Object other); declaration in the Comparable interface, you are now allowed to also provide the actual implementation in the interface.

Why would you need to do this? why not maintain the strict separation between defining a list of available operations in an interface and the actual implementation in an implementing class?

Being able to provide code implementation in an interface definition can result in less complex, cleaner, and more robust code. There are a couple of ways how this can be achieved.

Reduce complexity via static methods

Typically, as a service provider, when you provide interface that needs to be conformed to in order to access some specific services, you also need to provide some methods to provide some utility functions related to the service you provide.

An approach to follow will be to provide an interface and another utility class that contains the required utility functions. Any consuming objects that requires these services will need to first conform to the interface and then access the required functionalities from the accompanying utility classes.

A cleaner approach might be to create these utility functions directly in the interface as static methods, and as a result, the utility class is no longer needed.

A very good example of this can be found in the Java implementation of the Path interface and the corresponding Paths utility class

A Path interface represents an object that can be used to locate a file in a file system and it is composed of a hierarchical sequence of directory and file name elements.

Prior to Java 11, the companion utility class Paths provides the get method that can be used to construct a path to a file or directory from a string or URI.

However, from Java 11, this functionality is now part of the Path interface defined as static methods.

So the Paths utility class is no longer needed. You can now construct a path by calling the static of method of the Path interface

Path.of(“/foo/bar/gus”)

As a developer, when you implement your own interface, you don't need to provide separate companion class for utility methods, just move these method to the interface as static classes. This will ensure your code is less complex and cleaner.

Future-proof code with default method

Imagine you provide some services with some interface defined and some years later, you decided to add some new method to the interface when releasing a new version.

Adding a new method like this breaks all client codes that implement your interface and the class would no longer compile because there is no implementation for the new method.

A way to avoid this is by providing a default implementation for any interface method. This is known as interface evolution, and it ensures you are able to add new methods to your interface definition without breaking any existing code.

For example, consider the Iterable interface defined in Java. In Java 8, this interface was updated and a new method, forEach was added

Adding a new method like this will cause all the implementing classes of this interface not to compile and fail with compile errors. In order to avoid this error, this method was defined as a default method and a default implementation provided in the interface definition code.

References

Core Java Volume 1- Fundamentals, Eleventh Edition

Java Platform Version 14 API Specification

Conclusion

Taking advantage of the post- Java 8 ability to add code implementation to interface definition helps create cleaner and more robust code. I have tried to share some examples and insights to support adding code implementation to interfaces. Thanks for taking out time to read my article

--

--

Software engineer, with special love for anything object based, Java, C#, Swift, JavaScript