Interfaces In Java

A detailed guide to Java Interfaces and their properties with practical abstractions using Java Interfaces.

Overview

We use different controls and buttons on a car dashboard to get things done. Although the complex system under the hood takes care of a highly complicated job, we can relax, press a few buttons, and turn a few knobs. That is a perfect example of abstraction. The design engineers hide all the complexity behind an easy-to-interact interface.

An interface in Java is analogous to this dashboard. This article will learn about Java Interfaces, their features, and examples.

What is a Java Interface?

An Interface in Java is a blueprint for classes that enforces a class to provide certain behaviours. With interfaces, we can achieve abstractions, polymorphism, and multiple-inheritance without modifying the natural type structure of classes.

An Interface makes it mandatory for all implementations to override its non-default methods. Before we dive deeper, we will see a quick example of an Interface. In the following example, the Employee class implements Java’s Comparable interface.

public class Employee implements Comparable<Employee> {
  private Long id;
  private String name;
  private Integer age;

  @Override
  public int compareTo(Employee o) {
    return this.id.compareTo(o.id);
  }
}Code language: Java (java)

The class overrides the compareTo(employee) method from the interface. Now, we can use the Comparable interface to invoke the method on the class, which is what abstraction is.

Comparable<Employee> comparableEmployee = new Employee();
comparableEmployee.compareTo(anotherEmployee);Code language: Java (java)

Rules for Java Interfaces

Java Interfaces come with a set of rules we need to understand. Since Java 8, the interfaces can also include default methods. Using default methods, an Interface can provide a default behaviour so that the implementation can override it. For more information, please read Java Default Methods.

An interface can contain fields, abstract methods, static methods, private methods, and default methods.

Apart from that, the following list summarises the rules of the Java Interfaces.

  1. They don’t have constructors, and they cannot instantiate.
  2. We cannot make an interface or its methods final.
  3. All the methods in an Interface are implicitly public and abstract, and we can’t change that.
  4. However, they can have private non-abstract methods, including private static methods.
  5. An Interface’s fields are always public, static, and final.
  6. Interfaces can contain public or private static methods.
  7. They can have default methods, which use the default modifier.
  8. All an interface’s non-abstract implementations must override all its abstract methods.

Abstractions Using Interfaces

A Java interface can bring abstraction, just like the car dashboard we discussed in the beginning. One of the benefits of abstraction is that clients are free from knowing the internals of functionality, like ‘Who is doing it and How‘.

Let’s use an interface to create an example of abstraction. Following is the FlyingToy interface having two abstract methods.

public interface FlyingToy {
  void fly();

  void rest();
}Code language: Java (java)

Let’s create an implementation of this class.

public class Drone implements FlyingToy {
  @Override
  public void fly() {
    System.out.println("I am drone; I can fly");
  }

  @Override
  public void rest() {
    System.out.println("I am drone; I need rest too");
  }
}Code language: Java (java)

Now, a consumer of this abstraction can fly a drone or put it to rest without knowing any technicalities involved.

Flyable flyable = new Drone();
flyable.fly();
flyable.rest();Code language: Java (java)

Polymorphism using Interfaces

Polymorphism allows an object to exist in different forms at runtime. We can typically achieve this by ‘programming to interfaces‘ so that the client can continue invoking methods on the interface, and the actual implementation can attach at runtime.

To see that practically, we will create a FlyingToyFactory that returns a specific implementation of FlyingToy based on an environment variable.

public class FlyingToyFactory {
  private String env_flyingToyKey = "cow";

  public FlyingToy getFlyingToy() {
    if ("cow".equals(env_flyingToyKey)) {
      return new FlyingCow();
    }
    return new Drone();
  }
}Code language: Java (java)

Having this, our client is now completely decoupled from the interface implementations.

FlyingToyFactory factory = new FlyingToyFactory();
FlyingToy flyingToy = factory.getFlyingToy();
flyingToy.fly();Code language: Java (java)

The client is executing methods against the interface reference. Thus, exactly which implementation is invoked is decided only at runtime.

Multiple Inheritance using Interfaces

Java doesn’t support the multiple-inheritance feature because a class can extend one and only one class in Java. However, a class can implement any number of interfaces simultaneously. That helps us achieve multiple-inheritance to some extent.

As stated previously, interfaces introduce additional behaviour into a class without modifying its natural type structure. To demonstrate that, look at the following example.

public class Circle extends Shape implements Comparable<Circle> {
  private final Double radius;

  @Override
  public Double calculateArea() {
    return radius * radius * Math.PI;
  }

  @Override
  public int compareTo(Circle o) {
    return this.radius.compareTo(o.radius);
  }
}Code language: Java (java)

The Circle is a Shape type that perfectly overrides a method from its superclass. In addition, the class implements the Comparable interface, which gives it an additional behaviour – compareTo(Circle).

Thus the interface helped us add additional behaviour without changing the existing type structure. Moreover, we now state that the Circle IS A Shape, and the Circle IS A Comparable.

Comparable<Circle> comparableCircle = new Circle(12);
Shape shapeCircle = new Circle(12);Code language: Java (java)

As we can see, we have achieved multiple-inheritance by using a Java Interface.

Example of Abstract Class Implementing an Interface

An Abstract class can implement one or more interfaces like any Java class. The following example shows that our abstract ElectronicFlyingToy class implements the FlyingToy interface.

public abstract class ElectronicFlyingToy implements FlyingToy {
  public abstract void tunOn();

  public abstract void turnOff();
}Code language: Java (java)

An abstract class inherits all the abstract and default methods from the interfaces it implements. In our case, a concrete subclass of the ElectronicFlyingToy class must provide implementations for all the abstract methods from the abstract class and the FlyingToy interface.

Example of an Interface extending another Interface

Like classes, interfaces can inherit other interfaces. It is important to note that an interface can extend another interface (and not implement).

When an interface extends another interface, it inherits all the abstract and default methods from the super interface. The following snippet covers Java’s example of an interface inheriting another interface.

public interface ScheduledExecutorService 
    extends ExecutorService {

  ScheduledFuture<?> schedule(Runnable command, 
    long delay, TimeUnit unit);

  // ...other methods

}Code language: Java (java)

Unlike a Java class that can extend one and only one class, an interface can extend multiple interfaces. The following example shows the RunnableScheduledFuture interface extends two interfaces – RunnableFuture, and ScheduledFuture.

public interface RunnableScheduledFuture<V> 
    extends RunnableFuture<V>, ScheduledFuture<V> {
  
  boolean isPeriodic();
}Code language: Java (java)

Example of Private Methods in Interface

Java 9 introduced the private method feature into interfaces. The purpose of private methods is to bring encapsulation of common logic to avoid duplications.

public interface CanSumUp {
  default Integer addInts(Integer x, Integer y) {
    logAddNumbers(x, y);
    return x + y;
  }

  default Double addDoubles(Double x, Double y) {
    logAddNumbers(x, y);
    return x + y;
  }

  private void logAddNumbers(Number x, Number y) {
    System.out.println("Adding: " + x + ", " + y);
  }
}Code language: Java (java)

Similarly, we can also define private static methods in interfaces.

Summary

We covered a detailed and thorough discussion on Java Interfaces. We started by understanding Interfaces’ basic concepts, features, and rules. Also, with the help of examples, we understood how the interfaces help us achieve abstraction, polymorphism and multiple-inheritance. In the end, we covered examples of an abstract class implementing an interface and an interface extending one or more interfaces.

Refer to our Github Repository for the complete source code of the examples used in this tutorial.