Lecture 6

Enumerations

In a number of cases we want to have a type which can have only a certain number of predefined values. For example, there are exactly seven days in a week and they all have different names. The idiomatic way to model such situations in Java is by using enumerations. Similarly to the classes, each enumeration type is declared in a separate file:

public enum Day {
   MONDAY, TUESDAY, WEDNESDAY, THURSDAY,
   FRIDAY, SATURDAY, SUNDAY
}

The main difference is that now instead of the keyword class we use the keyword enum. As usual after enum we write the name of the new type. Inside the currly braces we give a sequence of names which will correspond to the possible values of this type. In our case the type is called Day and the possible values are the names of the days in a week.

We declare and use variables of an enumeration type in the usual way. For example the following declares the variable today and assigns the value MONDAY to it.

Day today = Day.MONDAY;

Note that the possible values of the type Day are accessed in the same way in which the Java constants are accessed, i.e. as static final variables. We always write the name of the class, i.e. Day followed by the name of the constant. The similarity is not a coincidence. The enumerations are a relatively new feature of Java and before that they were emulated by using constants. For example, this is how it would look like without using the special syntax for enumerations:

public class Day {
    public static final int MONDAY = 0;
    public static final int TUESDAY = 1;
    public static final int WEDNESDAY = 2;
    public static final int THURSDAY = 3;
    public static final int FRIDAY = 4;
    public static final int SATURDAY = 5;
    public static final int SUNDAY = 6;
}

i.e. every possible value is declared as an integer constant with a distinct value. If we want to declare a variable which takes as values the days of the week, then we just declare it as integer and we set it to one of the constants:

int today = Day.MONDAY;

The disadvantage of this is that it is not at all clear from the definition of the variable that it is supposed to represent a day of the week. Its type is just an integer and this does not tell much to the programmer. Furthermore, it becomes impossible for the compiler to check whether the variable always gets assigned a valid value. It will simply accept the meaningless assignment:

today = 13;

although we know that there is no week with fourteen days. This two problems are solved in an elegant way by using enumerations instead of plain constants. With enumerations the type of today is Day which immediately tells the programmer the intended use of the variable. Furthermore, the compiler is able to check whether we use it in the intended way. This means that the last assignment will be no longer acceptable when we use enumerations.

When we talked about the switch-case statement we said that it can be used only with primitive types such as integer, boolean and char, but actually it is also very useful with enumerations. For example you might want to do a different thing for the different days of the week:

switch (today) {
case MONDAY:
    System.out.println("Java lecture today!");
    break;
case TUESDAY:
case WEDNESDAY:
    System.out.println("No programming :-(");
    break;
case THURSDAY:
    System.out.println("Java lecture today!");
    break;
case FRIDAY:
    System.out.println("The end of the week");
    break;
default:
    System.out.println("Weekend at last :-)");
    break;
}

The switch-case statement works with enumerations in the same way as for the primitive types. The only specialty is that when you write the constants for the cases you don't write the type name, i.e. instead of case Day.MONDAY we write case MONDAY.

All enumeration types have a number of useful automatically generated methods. Two of them let us to convert values of enumeration types to strings and vice versa. Conversion to a string happens in the usual way, i.e. we call the method toString. This is how we print a value of enumeration type:

Day today = Day.MONDAY;
System.out.println("Today is "+today);

Of course internally this uses the toString method that we have discussed before. We can also call it explicitly:

String s = today.toString();

The opposite conversion from string to an enumeration is useful if we have to read a value of such type from the terminal. For this purpose the compiler generates the method valueOf which takes a string and returns the value with the matching name:

Scanner scanner = new Scanner(System.in);
Day today = Day.valueOf(scanner.next());

Another useful method is the method ordinal which gives us the position of the current value in the list given when the enumeration was defined:

System.out.println(today.ordinal())
For example MONDAY has index 0, TUESDAY has index 1, etc.

Finally we sometimes want to iterate over all possible values of an enumeration. For this purpose the compiler provides the method values which returns an array with all possible enumeration values. We can iterate over it in the usual way with a for loop:

for (Day d : Day.values())
    System.out.println(d);

Interfaces

The power of object oriented programming comes from the ability to do abstractions by grouping similar objects in classes and subclasses. In this way it is possible to develop generic algorithms which work on heterogenious objects as long as all objects share the same superclass. For instance, we can have a list of geometric objects, such as triangles, squares, circles, etc and we can write a generic algorithm for visualizing the list without the need to know how the particular shape looks like. It is enough if all the shapes share a common base class which has a method for visualization.

The level of abstraction that a given programming language allows is its most important characteristic. Higher abstraction in many cases makes the code shorter and easier to reuse. Since the main device for abstraction in object oriented programming is the subtyping, it sometimes seems necessary to write classes which simultaneously inherit two or more other classes. Unfortunatelly, the practice have shown that programming with multiple inheritance is difficult and for this reason Java has taken pragmatic but simple approach. There is no multiple inheritance in Java, every class can have only one base class! However, in addition to classes, the programmer can also define interfaces and each class can implement multiple interfaces.

The interface is like a contract between classes. It declares a list of method definitions, but there is no implementation for the methods and there are no instance variables. If we want to realize our example of an algorithm for visualizing lists of objects, then we can either enforce that all objects share a common base class or we can make it more general by using interfaces. Let's declare an interface for all visual things:

public interface Visual {
    void visualize();
}

This simple definition introduces the interface Visual which declares a single method visualize. Any class implementing the interface Visual will now be forced to implement the method visualize.

For instance we can define all geometric shapes as subclasses of class shape and for each shape we can implement the interface Visual:

public class Shape {}

public class Square extends Shape implements Visual {
    public void visualize() {
        System.out.println("A square");
    }
}

Note that we declare the base class with the keyword 'extends' but the implemented interfaces with the keyword 'implements'.

So far the use of an interface looks like a more complicated way to do something which we can do with a simple hierarchy. The situation changes completely, if we want to add another class which is not a subclass of Shape but is still Visual. For example we can have the class Chair which is a subclass of furniture. Despite that Square and Chair come from different branches in the class hierarchy they can still both implement the interface Visual:

public class Furniture {}

public class Chair extends Furniture implements Visual {
    public void visualize() {
        System.out.println("A chair");
    }
}

Now we can create an array of visual objects:

Visual[] visuals = new Visual[2];
visuals[0] = new Square();
visuals[1] = new Chair();

Note that we can use the name of the interface just like any other type, in this example we use it in the type of the array. After the creation of the array we initialize its elements. Although they are of different classes, they share the interface Visual and the compiler lets us to put them in the array.

Now it is trivial to handle all visual objects in an uniform way. We can just write a loop which traverses the array and calls the method visualize. It doesn't matter that the objects are of completely different types, since they all implement the same interface:

for (Visual v : visuals)
    v.visualize();

Example

The exam from 8 March 2010 has an example for using enumerations for modelling the state of a program. You can also find the code in the archive attached to the lecture.