3 reasons to start using Java interfaces

Posted on



what is an interface?

Oracle says: “an interface is a group of related methods with empty bodies”

I think of interfaces as a contract that a class promises the compiler to fulfill. If the class doesn’t do what’s promised the compiler will complain and give an error.



why use one?

Why bind myself to this contract and allow the compiler to yell at me about an unfulfilled promise?

Why don’t I just write the methods inside the class and be done with it?

After spending some time in the industry I started to form an understanding of why we use them. I will list the most important three reasons from my point of view.



1 – Document APIs

Let’s say your team is writing a new service to be added to the poll of services your company offers, and other teams will write some other services that are going to use your service.

How will they know what method to call? what parameters to pass? what return type to expect?

Well, you guessed it, it’s interfaces, the contract that ensures that those methods will always do as they describe in the interface.

Here is an interface that documents how to communicate with a registration service

public interface IRegistrationService {
    void registerNewUser(String userName);
    boolean userIsRegistered(String userName);
    void cancelRegistration(int registrationId);
    void cancelRegistration(String userName);
}
Enter fullscreen mode

Exit fullscreen mode

You would write your own implementation of this interface, and other developers will take a look at the interface and understand what to do.

public class RegistrationService implements IRegistrationService {
    public void registerNewUser(String userName) {
        // production code
    }
    public boolean userIsRegistered(String userName) {
        // production code
        return false; // the result of prod code
    }
    public void cancelRegistration(int registrationId) {
        // production code
    }
    public void cancelRegistration(String userName) {
        // production code
    }
}
Enter fullscreen mode

Exit fullscreen mode



2 – Write specifications

Imagine we want to write a specification of what something should do, but not write how it should be done, and leave that detail to whoever wishes to make it happen.

Perhaps the most famous example of this is the JPA (Java Persistence API)

JPA is a set of interfaces that defines how database persistence should look in Java applications. There are a couple of implementations of these interfaces

  • Hibernate
  • Toplink
  • EclipseLink
  • Apache OpenJPA
  • and many more

Let’s provide an example on “How to use a car” specification

public interface CarDrivingSpecification {  
        void startEngine();  
        void pressGas();  
        int getGasLeftInTank();  
        // can the car go for the kilometers provided, taking into consideration the gas left in the tank   
        boolean canGoForGivenKms(float kms);
    }
Enter fullscreen mode

Exit fullscreen mode

Here we only specify what will the class that implements this interface do, and not the actual implementation of how it does it.



3- Polymorphism

The third and most important use-case of interfaces is the fact that they allow us to leverage the powerful concept of Polymorphism.

  • Inheritance also allows Polymorphism. However, a class can only extend one superclass, while a class can implement as many interfaces as needed. That’s why it’s far more flexible.

This can make our code much more elegant and clean. But first, what was Polymorphism?

It allows us to say a thing like: “A student object can be treated as a Human Object” or “A Car object can be treated as a Vehicle object”.

But, how can Polymorphism help us write clean code? Let’s write some code that DOESN’T use Polymorphism.

Let’s start by declaring the entity classes
Bicycle class

import lombok.AllArgsConstructor;  
@AllArgsConstructor  
public class Bicycle {  
     private final int Id;  

     public void goForward(){  
         System.out.println("Bike forward");  
      }  
}
Enter fullscreen mode

Exit fullscreen mode

Car class

import lombok.AllArgsConstructor;  
@AllArgsConstructor  
public class Car {  
     private final int Id;  

     public void goForward(){  
          System.out.println("Car forward");  
      }  
     public void checkEngine(){  
         System.out.println("Checking Car engine");  
       }  
}
Enter fullscreen mode

Exit fullscreen mode

Truck class

import lombok.AllArgsConstructor;  
@AllArgsConstructor  
public class Truck {  
     private final int Id;  

     public void goForward(){  
          System.out.println("Truck forward");  
     }  
     public void checkEngine(){  
         System.out.println("Checking Truck engine");  
       }  
 }
Enter fullscreen mode

Exit fullscreen mode

Now let’s write the service that will utilize those classes and run the methods of every object

   public class TrafficService {  
    private List<Bicycle> getBicyclesFromMap(){  
        return Arrays.asList(new Bicycle(1), new Bicycle(2));  
    }  
    private List<Car> getCarsFromMap(){  
        return Arrays.asList(new Car(1), new Car(2));  
    }  
    private List<Truck> getTrucksFromMap(){  
        return Arrays.asList(new Truck(1), new Truck(2));  
    }  

    public void goForward(List<Bicycle> bicycles, List<Car> cars, List<Truck> trucks){  
        bicycles.forEach(Bicycle::goForward);  
        cars.forEach(Car::goForward);  
        trucks.forEach(Truck::goForward);  
    }  

    public void checkEngine(List<Car> cars, List<Truck> trucks){  
        cars.forEach(Car::checkEngine);  
        trucks.forEach(Truck::checkEngine);  
    }  

    public void makeAllTransportGoForward(){  
        goForward(getBicyclesFromMap(), getCarsFromMap(), getTrucksFromMap());  
    }  
    public void makeAllEnginesStart(){  
        checkEngine(getCarsFromMap(), getTrucksFromMap());  
    }  
}
Enter fullscreen mode

Exit fullscreen mode

First of all, we had to specify a method for each class of objects to get its instances from the map. Also, note that in the goForward method we had to pass as parameters the three types of objects (Bicycle, Car, Truck), and we had to call the goForward method for each class of objects.

Now, for three entities that isn’t a big problem. However, when you have 10 or even a 100 ones, that quickly becomes a problem.



Let’s apply interfaces and Polymorphism and see the results

We start by defining the interfaces,
The Transport interface

public interface Transport {  
      void goForward();  
}
Enter fullscreen mode

Exit fullscreen mode

The Vehicle interface which extends the Transport interface

public interface Vehicle extends Transport{
     void checkEngine();
}
Enter fullscreen mode

Exit fullscreen mode

The entities classes note that we make the classes implement the interfaces.

import lombok.AllArgsConstructor;  
@AllArgsConstructor
public class Bicycle implements Transport{
    private final int id;
    public void goForward() {
        System.out.println("Bike forward");
    }
}
// other class
import lombok.AllArgsConstructor;
@AllArgsConstructor
public class Car implements Vehicle{
    private final int id;
    public void checkEngine() {
        System.out.println("Checking Car engine");
    }
    public void goForward() {
        System.out.println("Car forward");
    }
}
// other class
import lombok.AllArgsConstructor;
@AllArgsConstructor
public class Truck implements Vehicle{
    private final int id;
    public void checkEngine() {
        System.out.println("Checking Truck engine");
    }
    public void goForward() {
        System.out.println("Truck forward");
    }
}
Enter fullscreen mode

Exit fullscreen mode

The service that utilizes the classes Polymorphismly

public class TrafficService {
List<Transport> getTransportFromMap(){
return Arrays.asList(
new Bicycle(1), new Bicycle(2),
new Car(1), new Car(2),
new Truck(1), new Truck(2));
}

List<Vehicle> getVehicleFromMap(){
return Arrays.asList(
new Car(1), new Car(2),
new Truck(1), new Truck(2));
}

public void goForward(List<Transport> transports){
transports.forEach(Transport::goForward);
}

public void checkEngine(List<Vehicle> vehicles){
vehicles.

Leave a Reply

Your email address will not be published. Required fields are marked *