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).
Sample Comparable<T> Code
import java.util.Arrays;import java.util.Collections;import java.util.List;public class Main { public static void main(String[] args) { List<Student> list = Arrays.asList( new Student(5, "middle"), new Student(1, "young"), new Student(10, "old") ); System.out.println(list.toString()); // [middle, young, old] Collections.sort(list); System.out.println(list.toString()); // [young, middle, old] }}public class Student implements Comparable<Student> { int age; String name; public Student(int age, String name) { this.age = age; this.name = name; } @Override public int compareTo(Student o) { return age - o.age; } @Override public String toString() { return name; }}
stdout
[middle, young, old]
[young, middle, old]
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 Studentseparately from the code in that Class.
The Collections Class in Java, also defines anothermethod 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.
Sample Compartor<T> Code
import java.util.Arrays;import java.util.Collections;import java.util.List;public class Main { public static void main(String[] args) { List<Student> list = Arrays.asList( new Student(5, "middle"), new Student(1, "young"), new Student(10, "old") ); System.out.println(list.toString()); // [middle, young, old] // Pass in an instance of Comparator that orders Students based on the length of their name Collections.sort(list, (o1, o2) -> o1.name.length() - o2.name.length()); System.out.println(list.toString()); // [old, young, middle] }}public class Student implements Comparable<Student> { int age; String name; public Student(int age, String name) { this.age = age; this.name = name; } @Override public int compareTo(Student o) { return age - o.age; } @Override public String toString() { return name; }}