Code for Interface, Not for Implementation Best Practice

In this article, we will discuss the best practice of coding to an interface, not to an implementation, with an example. Always program to an interface, not an implementation; this leads to flexible code that can work with any new implementation of the interface. This principle is advised in many Java books, including "Effective Java" and "Head First Design Patterns".

Check out the Java Best Practices series at Java Best Practices.

Video

The code for the "Interface, not for implementation" best practice is well explained in the video tutorial below:

Code: Program for Interface, Not for Implementation

Let's create a Shape interface with multiple implementations, such as Circle, Rectangle, and Square.

interface Shape {
    void draw();
}

class Circle implements Shape {
    @Override
    public void draw() {
        System.out.println("Drawing circle ...");
    }
}

class Rectangle implements Shape {
    @Override
    public void draw() {
        System.out.println("Drawing rectangle ...");
    }
}

class Square implements Shape {
    @Override
    public void draw() {
        System.out.println("Drawing square ...");
    }
}

Here, we use the interface as the reference type and method argument:

public class Test {
    public static void main(String[] args) {
        Shape shape = new Circle();
        print(shape);
    }

    private static void print(Shape shape) {
        shape.draw();
    }
}

Output:

Drawing circle ...

With the "code for interface, not for implementation" technique, you can change the implementation anytime without changing the existing code. For example, we can change from Circle to Square:

Shape shape = new Square();

Code for Interface, Not for Implementation in Collections Framework

1. Always use interface type as a reference type.

Example:

// Better
List<String> list = new ArrayList<>();

// Avoid
ArrayList<String> list = new ArrayList<>();

// Better
Set<String> set = new HashSet<>();

// Avoid
HashSet<String> employees = new HashSet<>();

// Better
Map<String, String> map = new HashMap<>();

// Avoid
HashMap<String, String> map = new HashMap<>();

By declaring a collection using an interface type, the code is more flexible as you can change the concrete implementation easily when needed, for example:

List<String> list = new LinkedList<>();

When your code is designed to depend on the List interface, you can swap among List’s implementations with ease without modifying the code that uses it.

2. Always use the interface type as a return type.

Example:

public Collection<Employee> listEmployees() {
    List<Employee> employees = new ArrayList<>();
    // add Employees to the list
    return employees;
}

3. Always use Interface Types as a method argument.

Example:

public void foo(Set<Integer> numbers) {
}

The flexibility of using interface type for a collection is more visible in the case of the method’s parameters.

Conclusion

Coding to an interface, not an implementation, is a crucial best practice in Java that promotes flexibility and maintainability in your codebase. This approach ensures that your code can easily adapt to new implementations without significant changes.

Check out more Java best practices series at Java Best Practices.

Java Best Practices

Comments