Program to the Interface

There is a principle behind everything we have done in this chapter: program to the interface, not the implementation.

In practice this is a habit about types. When you write code that uses a list, whether it is a variable, a method parameter, a return type, or a field, declare it with the ADT type, List, and not with a concrete implementation type like ArrayList or LinkedList. You still have to choose an implementation at the point where you build the object with new. But everywhere else, you refer to it through the interface.

List<Integer> numbers = new ArrayList<>();
selectionSort(numbers);

We have already been doing this without naming it. selectionSort takes a List<T>, not an ArrayList<T>. It depends only on what a list promises, not on how a particular list is built.

This is the right habit because, when we declare variables with the interface type, we decide what the code is allowed to rely on. When the declared type is List, the code can use only what the List contract provides, so it cannot accidentally come to depend on a detail of one implementation. Implementations then become interchangeable:

List<Integer> numbers = new LinkedList<>();
selectionSort(numbers);

selectionSort is written against the contract, and both implementations keep the contract. In the future, if we implement a new list, we can use it with selectionSort without changing the code of selectionSort at all.