CSE5910 : Multimedia Programming in Java

Lecture : Interfaces, Generic Types and Enumerated Types

In the previous lecture:

In this lecture:



Interfaces

Interfaces are Java reference types just like classes.

However, interfaces define no implementation details for the types they define, all of their methods are declared abstract. This means that they consist of headers with no implementation code inside them!

It is therefore not possible to directly instantiate an object of an interface type since no code has been written in the interface to say how a member of such a type could possibly behave.

Interfaces only provide a list of methods that another class may implement. It is then possible to instantiate an object of this class type that implements the interface.

Any class that implements all of the methods of an interface is said to implement the interface. Such a class is a member of the class that it defines, any class that it extends, and also it is a member of any of the (multiple) interfaces it implements.

If a class implements an interface we can be sure that it has a consistent set of public methods in common with all other classes that implement that interface.

The methods of an interface are necessarily public. There is no need to include the keyword public in the method declarations.

// This is an interface definition. A Point2D could implement this interface
// by actually writing code for methods setLabel, getLabel and resetLabel.
// Note that since an interface cannot be instantiated, it does not define constructor.

public interface Labelled
{
   void setLabel(String newLabel);
   String getLabel();
   void resetLabel();
}
// Here is a different interface that, if implemented by a class, ensures that the
// class will be "printable"... i.e. the interface just ensures that any class that
// implements it has a Print() method with parameters and return type that match the
// one specified here.

public interface Printable
{
   void Print();
}
/** Point2D represents a labelled Cartesian point in 2 dimensions */
// Note: this class looks just like the one that we wrote in lecture 4a... it actually already *did*
// implement the Labelled and Print interfaces. Now it can advertise the fact though and the compiler will
// check that the interfaces are fully implemented for us because of the "implements Labelled, Printable"
// statement in the class declaration.
public class LabelledPoint2D extends Point2D implements Labelled, Printable
{
   private static final String defaultLabel = "untitled";
   private String label;

   public LabelledPoint2D()
   {  label = defaultLabel;  }
   public LabelledPoint2D(double xCoordinate, double yCoordinate, String label)
   {
    super(xCoordinate, yCoordinate);
    this.label = label;
   }

   public void setLabel(String newLabel)
   { label = newLabel; }

   public String getLabel()
   { return label; }

   public void resetLabel()
   { label = defaultLabel; }
   
   public void Print()
   { System.out.println("label: " + label);
     super.Print();
   }
}

Do you remember the notes on Genericity from lecture 1b?

(To recap) Genericity

Abstraction of structure independently of type is genericity (pronounced jen-err-iss-it-ee).

Example: Compare these two functions to return the maximum of two numbers.

integer maxInteger (integer param1, integer param2)
{
if (param1 > param2)
{ return param1; }
return param2;
}
float maxFloat (float param1, float param2)
{
     if (param1 > param2)
     { return param1; }
     
     return param2;
}

It seems silly to have to write the two functions but if a language doesn't allow you to pass an integer to a function that requires a floating point parameter (or vice versa) then this is exactly what you must do.

Some languages allow you to to write a single generic function that will serve the same purpose for both integers and floating point values (or any other types you like for that matter). In general, such a function might look a little like this.

TYPE max (TYPE param1, TYPE param2)
{
if (param1 > param2)
{ return param1; }
return param2;
}

In this case we have maintained the common structure of the "max" functions and abstracted away the differences in their type information to produce a general function that can serve the purposes of both maxInteger() and maxFloat(). In this case we are depending on the overloading of the ">" operator to work correctly for integers and floating point variables alike.

The word TYPE is set up by the programmer to represent different data types.

Example: Suppose we wanted to create a container class that stored a

list of Point2Ds... ...and another that stored a list of Strings...
public class ListOfPoint2Ds
{
   // implement list as an array of Point2D
   private Point2D [] theList; 

   public ListOfPoint2Ds (int listSize)
   {   theList = new Point2D [listSize];   }

   public void PrintList ()
   {
      for (int i=0; i<theList.length; i++)
      {   System.out.println(" " + theList[i]);   }
   }

   public void SetValue (Point2D newValue, int listItemNumber)
   {   theList[listItemNumber] = newValue;   }
}
public class ListOfStrings
{
   // implement this list as an array of String
   private String [] theList;
   
   public ListOfStrings (int listSize)
   { theList = new String [listSize]; }

   public void PrintList ()
   {
       for (int i=0; i<theList.length; i++)
       { System.out.println(" " + theList[i]); }
   }

   public void SetValue (String newValue, int listItemNumber)
   { theList[listItemNumber] = newValue; }
}

...isn't it annyoing that to do this we need to re-write the code each time we want a list of something different? The list classes are so close to being the same. A generic type would allow us to avoid this problem. Java 5.0 supports generic types.


The Generic ArrayList<E> Type

Java's generic container types are included in the java.util package.

Containers, for example Lists and Sets, are reference types that hold other reference types.

To use a generic container type in Java we place the type of elements we want the container to hold between < and >.

List <Point2D> pointList = new ArrayList<Point2D>();
List <String> stringList = new ArrayList<String>();

These two instructions define Lists (implemented as an array by the class ArrayList) that hold Point2Ds and Strings respectively.

The ArrayList class is defined for you in java.util like this:

public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, Serializable { ... }  

This means that ArrayList<E> is derived from the class AbstractList<E>. The class names after implements refer to the interfaces that ArrayList<E> implements.

Because it implements a List<E> using as an array, ArrayList<E> has to internally manage the size of the array in order to fit all the elements of the list. If the list gets too big for the array for example, the class needs to make a new, larger array to store the list elements. If the list is very short but the array holding it is very large, a lot of space is wasted. The class offers a mechanism to reallocate a smaller array that neatly fits the list.

These internal mechanisms are encapsulated in the class... the user of the class need not worry about them. However, the class provides public methods that allow the programmer using the class to optimise its behaviour.

public ArrayList(int initialCapacity);       // constructor that allows the programmer to specify initial capacity
public void ensureCapacity(int minCapacity); // method to set the minimum capacity requirement
public void trimToSize();                    // method to reallocate the internal array to exactly the current size

ArrayList<E> implements all of the interface List<E> which requires it to have the following methods (and more):

public void add(int index, E element);       // add an element of the specified type E at location 'index'
public void clear();                         // empty the list
public E remove(int index);                  // remove an element (and return its value)
public E get(int index);                     // get the value of an element
public boolean contains (Object elem);       // check if the list contains an elem
public int size();                           // the size of the list

Check a reference manual for all of the methods available in the class. Here's a little example for you to play with:

public import java.util.*;
public class ListExample
{
   public static void main(String[] args)
   {
      List<String> strList = new ArrayList<String>();
      String javaString = "Java";
      strList.add("I love");
      strList.add("Lists");
      strList.add(javaString);
      strList.add("Lists");
      System.out.println("List size = " + strList.size());
      for (String tmpString : strList)
      {   System.out.println("List element = " + tmpString);   }
      strList.remove("Lists");
      System.out.println("-- removed \"Lists\"");
      System.out.println("List size = " + strList.size());
      for (String tmpString : strList)
      {   System.out.println("List element = " + tmpString);   }
      strList.clear();
      System.out.println("-- cleared list");
      System.out.println("List size = " + strList.size());
}

Output:


List size = 4
List element = I love
List element = Lists
List element = Java
List element = Lists
-- removed "Lists"
List size = 3
List element = I love
List element = Java
List element = Lists
-- cleared list
List size = 0

Try the example above using the class's other methods also. What other generic types does Java 5.0 support? What are Collections (such as Lists, and Sets) and Maps? How do they work? Try them out in your own time. (E.g. see p235-238, Flanagan)

Tip

Java's generic container classes can only hold reference types (not primitive types like int, char or double). Hence it is possible to make an ArrayList<String> but not an ArrayList<int>.

There is a way around this limitation: Java allows us to "box" a primitive int in a reference type Integer, a primitive boolean in a reference type Boolean and so on for the other primitive data types. Java handles the conversion of the primitive type to its corresponding reference type neatly and automatically allowing us to store the reference type in a generic container and then extract from the container the value to be used as if it was a primitive type. So it is fine to create an ArrayList<Integer> and we can then work with ints.

Look up "boxing" and "unboxing" in your text book (e.g. p88-89 in Flanagan) or favourite online reference for details.


Enumerated Types

An enumerated type is a reference type in Java.

public enum WeekDay { MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY }

WeekDay today = WeekDay.Tuesday;

Above we define an enumerated type WeekDay and a variable of that type.

A variable of an enumerated type may only adopt values listed (enumerated) between the { and }. By convention the names of the alternative values are capitalised.

You can write methods for enumerated types just like those written for classes.

Enumerated types are type-checked by Java just like types of classes.

Some sample code is provided for you to see how enumerated types may be used.



Lecture summary:


CSE5910 Courseware | CSE5910 Lecture Notes