slanted W3C logo

Day 27 — Interfaces

Java has two mechanisms for defining a type that has multiple different implementations.

The first mechanism is abstract classes. An abstract class serves as the superclass for the multiple implementations (the subclasses). The subclasses inherit from (extend) the abstract superclass.

The second mechanism is interfaces. Interfaces are different from inheritance. An interface defines the methods that a class must implement.

Both abstract classes and interfaces allow a client to write code based on a single API.

Interfaces

An interface is a group of one or more methods that a class promises to implement. For example, objects that have a natural order (numbers, strings, dates, etc) usually promise to implement the Comparable interface:

// does String s come before t (dictionary order)?
if (s.compareTo(t) < 0)
{ 
   // do something 
}
// is Fraction f smaller than g?
if (f.compareTo(g) < 0)
{ 
   // do something 
}
// is Date d before e?
if (d.compareTo(e) < 0)
{ 
   // do something 
}

Comparable

public interface Comparable<T>
{
   int compareTo(T other);
}

The Comparable interface says that any class that implements the interface must provide a method named compareTo that returns a negative integer, zero, or a positive integer as this object is less than, equal to, or greater than the specified object.

An interface is not a class; you cannot create an instance of an interface. Instead, you must find a class that implements the interface and create an instance of that class.

Interfaces Have APIs

Just like a class, each interface has its own API.

Comparable API

Classes that Implement Interfaces

You can tell if a class implements an interface based on its API:

public final class String
extends Object
implements Serializable, Comparable<String>, CharSequence

Notice that unlike inheritance, a class may implement multiple interfaces.

Interfaces are Types

Even though Comparable is not a class, it is still a valid type; thus, a String is-a Comparable. You can even create a reference to a Comparable object:

public class Interface
{
   public static void main(String[] args)
   {
      Comparable<String> s = "hello";
      System.out.println(s.compareTo("goodbye"));
   }
}

This is an odd example, but you will see many more examples in Chapter 10.

Classes that Implement Interfaces

You have already seen the Iterable interface; most collection-like classes implement this interface.

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

The Iterable interface is what allows you to write for-each loops:

// gc is a GlobalCredit

for (CreditCard cc : gc)
{
   output.println(cc.getNumber());
}
// pf is a Portfolio

for (Investment inv : pf)
{
   output.println(inv.getStock());
}
// stu is a Student

for (String course : stu)
{
   output.println(stu.getCourseGrade(course));
}

Fun fact: Iterator is itself an interface.

The Java Collection Framework

Collections

In Chapter 9 we studied three different collections:

Java Collection Framework

Collections are used often when writing code. It would be very tedious and error prone if a client had to create their own collection classes. Java provides a set of software components that are used to represent and manipulate collections of any object type. The Java Collection Framework is made up of:

The official tutorial for Java collections is http://download.oracle.com/javase/tutorial/collections/index.html .

UML Diagram

The UML diagram for the Collection interface hierarchy is shown below:


UML Diagram

The Collections Framework also includes a hierarchy of interfaces that are not rooted at Collection; nevertheless, the Map interface is an important part of the Collections Framework.


Collection Interface

A Collection represents a group of objects where each object is called an element of the collection. The interface defines the most general operations that a client can ask a collection to perform:

public interface Collection<E> extends Iterable<E>
{
   // Basic operations
   int size();
   boolean isEmpty();
   boolean contains(Object element);
   boolean add(E element);                      //optional
   boolean remove(Object element);              //optional
   Iterator<E> iterator();

   // Bulk operations
   boolean containsAll(Collection<?> c);
   boolean addAll(Collection<? extends E> c);   //optional
   boolean removeAll(Collection<?> c);          //optional
   boolean retainAll(Collection<?> c);          //optional
   void clear();                                //optional

   // Array operations
   Object[] toArray();
   <T> T[] toArray(T[] a);
}

Collection Interface

public interface Collection<E> extends Iterable<E>
{
   // Basic operations
   int size();
   boolean isEmpty();
   boolean contains(Object element);
   boolean add(E element);                      //optional
   boolean remove(Object element);              //optional
   Iterator<E> iterator();

   // Bulk operations
   boolean containsAll(Collection<?> c);
   boolean addAll(Collection<? extends E> c);   //optional
   boolean removeAll(Collection<?> c);          //optional
   boolean retainAll(Collection<?> c);          //optional
   void clear();                                //optional

   // Array operations
   Object[] toArray();
   <T> T[] toArray(T[] a);
}

Collection Interface

public interface Collection<E> extends Iterable<E>
{
   // Basic operations
   int size();
   boolean isEmpty();
   boolean contains(Object element);
   boolean add(E element);                      //optional
   boolean remove(Object element);              //optional
   Iterator<E> iterator();

   // Bulk operations
   boolean containsAll(Collection<?> c);
   boolean addAll(Collection<? extends E> c);   //optional
   boolean removeAll(Collection<?> c);          //optional
   boolean retainAll(Collection<?> c);          //optional
   void clear();                                //optional

   // Array operations
   Object[] toArray();
   <T> T[] toArray(T[] a);
}

A Problem of Type

Recall that GlobalCredit was a collection of credit cards and Portfolio was a collection of investments. In general, a collection needs to be able to hold elements of some type E.

How might we create a collection that can hold any type E? We know that every class has Object at the root of its inheritance hierarchy, so a possible solution is to create a collection that holds Object references (because every class type is substitutable for Object).

A Problem of Type

There are two significant problems with the collection of Object approach.

The first problem is that every class is substitutable for Object. This means that a client can put anything into a collection that holds Object references. If the client creates a collection of Strings there is nothing preventing the client from adding a Fraction to the collection.

The second problem is that such a collection will always return a reference to an Object whenever the client retrieves an element from the collection. This means that the client must always try to cast the type of the retrieved element to do anything remotely useful.

Generics

The designer of the Java language solved the problem by creating a mechanism called generics that allows the client to specify the type of element to use. Suppose you wanted to create a collection of strings:

Collection<String> someStrings = new ArrayList<String>();
      
// add a string
someStrings.add("Hey this works!");

Here's how you read the notation:

  Collection<String>     Collection of String  
  ArrayList<String>     ArrayList of String  
  List<String>     List of String  

You can only use generics if the class or interface is declared as a generic interface (ie. don't try this with Fraction or String).