Java SE 8 Documentation
Exercises

Interfaces

Objects can only communicate with the outside world through the fields and methods that they make available to that. Those fields and methods form the interface of the given object. (For what it is worth, objects already had those interfaces before, but we have not called it like that yet.) With the help of subtyping, it is possible to define interfaces as standalone types and connect them to all the other types that implement them as their subtypes. By implementing an interface, we mean that the interface has to declare (and actually it also logically groups) fields and methods that must be available with their values and bodies in the classes that are the subtypes of that.

Interfaces may be also considered as a new reference type, a collection of signatures for methods and class-level constants. That introduces another level of abstraction where there is the chance to make programs independent of their actual implementations.

Interfaces are defined in a manner similar to that of classes, but there the interface keywords has to be applied:

interface Comparable<T> {
    /* public abstract */ int compareTo(T object);
}

Members of an interface can also be defined like they were placed to a class, but some care must be taken with regard to the following:

  • Every field can only be public, static, and final. Since that is implicitly assumed, there is no need to add those modifiers, though they may improve the readibility. (The final modifier is going to be elaborated below.)

  • Every method can only be public and abstract. (The abstract modifier will be discussed later.)

As a consequence, every field must be initialized with a value that is known in compile time (otherwise it will result an error). In addition to that, definitions may not contain the this and super references, because all the fields are static.

interface I {
    int a = b; // error
    int b = 0; // that is public static final
    int c;     // error
}

Interfaces are mostly used for describing properties and behaviour, and it is allowed to implement many of them. When a class wants to implement an interface, it has to be declared with the implements keyword, similarly to the case when a class was derived from another one with extends.

For example:

class Time extends Object implements Comparable<Time> {
    private int hour;
    private int minute;
    private int second;

    /* ... */

    // Methods to override that are inherited from java.lang.Object:
    @Override
    public boolean equals(Object other) {
        if (other instanceof Time) {
            Time t = (Time) other;
            return (this.compareTo(t) == 0);
        }
        return false;
    }

    @Override
    public int hashCode() {
        return timeOfDay();
    }

    // Method to override that is expected by Comparable<Time>:
    @Override
    public int compareTo(Time other) {
        return new Integer(timeOfDay()).compareTo(other.timeOfDay());
    }

    private int timeOfDay() {
        return (hour * 3600) + (minute * 60) + second;
    }
}

It is possible to derive interfaces from other interfaces by the extends keywords that we used for classes, but there multiple base interfaces may also be given:

interface ThreadPolicy extends ThreadPolicyOperations, Policy, IDLEntity {}

That is called multiple inheritance, which is not allowed for classes. This way, the designers of the language wanted to work around the so-called diamond problem, that is, when multiple implementations for the same method are inherited from multiple base classes. On the contrary, it is good to know that there cannot be cyclic references between interfaces via the inheritance relation, and this will result in a compile-time error.

Note that interfaces do not have a common base like it was java.lang.Object for classes, so when a new interface is defined without any base or elements, it will not have any elements at all. At the first sight, this does not make too much sense — but given that classes may implement multiple interfaces at the same time, such interfaces can be exploited for marking classes. An example of that is the java.io.Serializable interface that declares that the class in questions can be converted a sequence of bytes.

interface Serializable {}

Because an interface is a base type for all the classes that implements it, it is possible to define methods that will expect not objects of a certain type, but objects having a certain property. As an example, consider the sort() method from the java.util.Collections class:

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

This signature means that the methods can accept any type that is capable of acting like a list that stores elements of type T. All the expected operations are described by the java.util.List interface. But because we want to sort the elements of this "list", the T, that is, the type of the elements of the list is constrained: T must be comparable. This can be achieved through implementing the Comparable<> interface with any type (that is denoted by the ?) that is a base type of T (super). Obviously, T is a base of itself, so Comparable<T> is also allowed, but only that one. Unfortunately, here extends has to be used in place of implements, which is a bit counter-intuitive.

A deliberately incomplete definition of List<T> that was used in the example might be as follows:

interface List<T> extends Collection<T>, Iterable<T> {
    boolean add(T e);
    void add(int index, T element);
    boolean addAll(Collection<? extends T> c);
    boolean addAll(int index, Collection<? extends T> c);
    void clear();
    boolean contains(Object o);
    boolean containsAll(Collection<?> c);
    T get(int index);
    boolean isEmpty();
    Iterator<T> iterator();
    T remove(int index);
    boolean removeAll(Collection<?> c);
    T set(int index, T element);
    int size();
    List<T> subList(int fromIndex, int toIndex);
    <E> E[] toArray(E[] a)
    /* ... */
}

As part of List<T>, there we can discover three more interfaces. One of them is the base interface, which is java.util.Collection, that captures a more generic description of data structures capable of storing objects. Another two is introduced through java.util.Iterator that can be created by the iterator() method, which is prescribed by java.lang.Iterable:

interface Iterable<T> {
    Iterator<T> iterator();
}

interface Iterator<T> {
    boolean hasNext();
    T next();
    void remove();  // optional
}

Iterators make it possible to traverse a data structure, and, optionally, to remove elements from there. An iterator always has to start walking the elements from the beginning of the structure, and then, with the help of the next() method, the individual elements might be queried while the traversal proceeds. The remove() method may be used to delete the element at the actual position, but that is optional, so it may not be implemented by the given iterator at all.

A simplified version of iterators are the enumerators where removal of elements is not possible. That is the java.util.Enumeration interface. But that is considered deprecated in the newer revisions of Java, so the Iterator is used and recommended everywhere. If the remove() is not desired to be supported, an UnsupportedOperationException could be thrown in the body.

Note that the "for each" construct actually translates to the use of an iterator for the affected type. That is, the following the expression:

List<Integer> list = new ArrayList<Integer>();

for (Integer n : list) {
    System.out.println(n);
}

can always be rewritten like as follows (which is done by the compiler):

Integer n;
for (Iterator iterator = list.iterator(); iterator.hasNext(); System.out.println(n)) {
    n = (Integer) iterator.next();
}

In addition to that, note that this interface is also suitable for capture the common operations for the previously learned LinkedList<T> and ArrayList<T> classes. For what it is worth, they were already written with that interface in mind. In result, List<T> can be always replaced with them (due to subtyping).

List<Integer> listOfIntegers = new ArrayList<Integer>();

That is why it is expedient to use the abstract (interface) type on the left hand side of the equation, and use a concrete implementation, that is, a subtype on the right hand side. Since List<T> is an interface, it cannot be used on the right hand side at all.

The final Modifier

We have seen above that fields in interfaces can only be final ones. For a field, the final modifier means that it is a constant, and it can be assigned only once. So, with the help of this modifier, constants may be created whose names are all written in uppercase letters. And because this will make a field immutable, that is fine to declared it to be public as well. When constants are defined, it does not make much sense to have them for every instance of the class (unless there is a reason for that), so they are often declared as class-level ones.

For example:

public static final int MIN_AMOUNT = 0;
public static final int MAX_AMOUNT = 100;

Another use of the final modifier is to forbid derivation of a class, or to forbid overriding methods.

final class Unextendable {}
class Naive extends Unextendable {}  // that is impossible

or:

class Some {
    final int unoverridable() { return 42; }
}

class Naive extends Some {
    @Override
    int unoverridable() { return -1; }  // that is impossible
}

Exercises

  • Implement a simplified version of the List<T> interface for the previously defined Store<T> class.

  • As part of the interface, define an iterator for the Store<T> class.

  • Create the Stack<T> generic interface that declares operations for a stack (such as add element to the top of the stack, remove element from the top of the stack, get the value of the topmost element, get iterator, get size, emptying the stack, maximal size as a constant, having exceptions thrown in case of errors).

  • Define the ArrayStack<T> and LinkedStack<T> generic classes that are both implementing the Stack<T> generic interface. The former represents the stack as an array and it can work with a fixed size, while the latter uses a linked list for implementing the stack and it can grow dynamically over time.

  • With the help of Stack<T> and any of its implementation, write a program that implements a simple stack-based calculator. The stack calculator takes a postfix expression and evaluates it. For example, a postfix expression looks like as the following:

    1 2 + 3 *

    that is actually corresponds to this infix expression:

    (1 + 2) * 3
  • Implement the Comparable<T> interface for the previously implemented Date class.

Related sources
Front page
Back