• Interface Inheritance creates an “has-a” relationship
  • Each Class can implement any number of Interfaces
  • Fields can only be, and are public static final by default
  • Methods are public by default and can explicitly be private (>= Java 9)
  • Methods are only signatures and have no implementation body unless they are static or default
  • Methods can be static or default which require concrete implementation. see here

Purpose:

Interfaces in Java actually have 2 main purposes !!

Behavioural/Contractual Interfaces

Interfaces are often described as contracts. While classes describe a whole ‘thing’, Interfaces only describe behaviour. A Cat should be described by a class, it has properties like hungerLevel and behaviour like eat() that relate to each other. But we could implement an Interface called Trained which describes behaviours like Sit() or RollOver(). Now our Cat is able to sit and roll-over! But of-course, these behaviours are not exclusive to Cat, they can be implemented by Dog, Cow… whatever! That’s the point of interfaces. To assign specific shared behaviours you want your classes to have.

A key example of a Behavioural Interface from the Java standard library is Comparable<T>.
This Interface defines the behaviour/method int compareTo(T o) which the implementing Class then has to Override, giving it the behaviour of being comparable!
int compareTo(T o) is used by an object to compare itself to another object. Giving objects of this Class a natural order.

The Collections Class in Java, defines a method named sort which has the following signature:

public static <T extends Comparable<? super T>> void sort​(List<T> list)

This is a generic method using generic wildcards to express that its parameter List<T> list can only take in a List which has elements that have a natural order, which is required for the sorting algorithm. It does this by enforcing the generic T of the list must be of a type/Class that implements Comparable<T>.

So, by implementing the Comparable<T> Interface, we will have given the Class and its objects a natural order which will allow us to sort it by calling Collections.sort(List<T> list).

Functional Interfaces

It is often useful to be able to pass some logic into a function. Especially when it comes to sorting.
Perhaps the objects I am sorting have no natural order (doesn’t implement Comparable<T>), or I want to define my own custom order!

This is where Functional Interfaces come into play! Unlike Behavioural Interfaces, these interfaces are used solely to pass functions around. They should not be implemented by a Class, as they are meant to represent a function on its own, and not a behaviour that a Class can exhibit.
Instead, they should be implemented with the lambda syntax or method reference.

The Functional Interface from the Java standard library Comparator<T> is what is used for implementing a custom sorting order.
This Functional Interfaces defines the method int compare(T o1, o2) which compares two objects of the same Class/type, allowing us to define the order of objects of a Class Student separately from the code in that Class.

The Collections Class in Java, also defines another method named sort which has the following signature:

public static <T> void sort(List<T> list, Comparator<? super T> c)

This is also a generic method using generic wildcards. However, instead of restricting the generic T to be of a type that has natural order (implements Comparable).
This method allows us to pass in a List<T> list of any type, but we must also supply Comparator<? super T> c, which is an implementation of Comparator<T> that is compatible with the type of the elements in the List<T> list.

So, by supplying an implementation of Comparator<T> to Collections.sort(List<T>, Comparator<? super T> c) we can sort any list of any elements in any way we want, even if the element itself is not of a type that has natural order.