Font Awesome Free 5.13.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)

Interfaces (Java)

An overview of interfaces in Java

Basics

Main idea behind interfaces: specify the contract between supplier(s) of certain functionality and client code which uses that functionality

Most common use case:

  • Define an interface declaring one or more abstract methods (without implementation)
  • There may be several classes actually implementing the interface (and specifying concrete implementations for the methods)
  • Client code can then require an object that conforms to a certain interface, without having to care about which implementing class the object is an instance of

An interface can contain:

  • Public static constants
  • Public abstract methods
  • Nested classes, interfaces, enums and annotations (often not considered good practice)
  • Default methods (since Java 8, see below)
  • Private methods (since Java 9)
    • Can be used by default method implementations specified in the interface
  • Static methods (since Java 9)

Default methods

Default method: method in an interface that specifies an actual implementation.

Classes implementing an interface with default methods can choose to use that implementation (this is what happens by default) or override it with custom implementation.

Use cases:

  • Backwards compatible extension of interfaces
    • Example: the introduction of streams in Java 8
      • Starting from Java 8, the Collection interface now has a .stream() method
      • If that method would have been added to the Collection interface as an abstract method, this would have meant that all of the custom Collection classes that people had implemented would fail to compile until their authors added that .stream() method. Additionally, if such a class was supplied in a JAR file (for example, a reusable library) that was compiled with an earlier version of Java, the class would still load successfully but users would suddenly get runtime errors on calling the .stream() method.
  • Cleaner alternative to the older pattern of declaring an interface (with only abstract methods) and then offering an abstract companion class providing default implementations for most of the methods
    • Example of that older pattern: Collection and AbstractCollection

Abstract classes vs. interfaces

As interfaces have become more powerful, they have become more similar to abstract classes.

Still, some differences remain:

  • Interfaces cannot be instantiated
    • Every object has to be an instance of an actual class
    • Unlike abstract classes, interfaces cannot have any instance variables or constructors
      • Interfaces are for specifying behavior, not for encapsulating state
  • Static variables on interfaces can only be final
  • A class can only extend at most one abstract class, but a class can implement any number of interfaces
    • Makes sense: an interface is intended to specify a contract and a class could potentially conform to multiple contracts

Default methods and inheritance

Problem: Since interfaces can now also contain method implementations, it is possible that the class/interfaces that a class extends/implements contain several implementations for the same method signature. What do we do in this case? This is known as the diamond problem .

How Java solves this:

If two interfaces that a class implements specify a method with the same signature, you must always provide an implementation.

  • Even if only one of the interfaces provides a default implementation, the compiler forces you to be explicit about the specific implementation to be used
    • If one of the default methods already specifies the behavior you want, your implementation can simply delegate to it
  • If none of the interfaces has a default implementation, the implementing class must provide an implementation anyway
public interface InterfaceA {
public abstract void doSomething();
}
public interface InterfaceB {
public default void doSomething() {
System.out.println("test");
};
}
public class Class implements InterfaceA, InterfaceB {
// compiler forces us to provide an implementation
@Override
public void doSomething() {
// delegate to behavior specified in InterfaceB
InterfaceB.super.doSomething();
}
}

If a class inherits from a superclass implementing a certain method but also implements an interface that has a default method with the same signature, the implementation from the superclass will always have priority

  • This prevents default methods from changing the behavior of code that was written before Java 8.

Resources