Extending Enum Types: Constant-Specific Class Bodies – Object-Oriented Programming

Extending Enum Types: Constant-Specific Class Bodies

Constant-specific class bodies define anonymous classes (§9.6, p. 521) inside an enum type—they implicitly extend the enclosing enum type creating new subtypes. The enum type Meal in Example 5.26 declares constant-specific class bodies for its constants. The following skeletal code declares the constant-specific class body for the enum constant BREAKFAST:

Click here to view code image

BREAKFAST(7,30) {                     // (1) Start of constant-specific class body
  @Override
  public double mealPrice(Day day) {  // (2) Overriding abstract method
    …
  }
  @Override
  public String toString() {          // (3) Overriding method from the Enum class
    …
  }
}                                     // (4) End of constant-specific class body

The constant-specific class body, as the name implies, is a class body that is specific to a particular enum constant. As for any class body, it is enclosed in curly brackets, { }. It is declared immediately after the enum constant and any constructor arguments. In the code above, it starts at (1) and ends at (4). Like any class body, it can contain member declarations. In the above case, the body contains two method declarations: an implementation of the method mealPrice() at (2) that overrides the abstract method declaration at (7) in the enclosing enum supertype Meal, and an implementation of the toString() method at (3) that overrides the one inherited by the Meal enum type from the superclass java.lang.Enum. The @Override annotation used on these overriding methods ensures that the compiler will issue an error message if such a method declaration does not satisfy the criteria for overriding.

The constant-specific class body is an anonymous class—that is, a class with no name. Each constant-specific class body defines a distinct, albeit anonymous, subtype of the enclosing enum type. In the code above, the constant-specific class body defines a subtype of the Meal enum type. It inherits members of the enclosing enum supertype that are not private, overridden, or hidden. When the enum type Meal is loaded at runtime, this constant-specific class body is instantiated, and the reference value of the instance is assigned to the enum constant BREAKFAST. Note that the type of the enum constant is Meal, which is the supertype of the anonymous subtype represented by the constant-specific class body. Since supertype references can refer to subtype objects, this assignment is legal.

Each enum constant overrides the abstract method mealPrice() declared in the enclosing enum supertype—that is, provides an implementation for the method. The compiler will report an error if this is not the case. Although the enum type declaration specifies an abstract method, the enum type declaration is not declared abstract—contrary to an abstract class. Given that the references meal and day are of the enum types Meal and Day from Example 5.26, respectively, the method call

meal.mealPrice(day)

will execute the mealPrice() method from the constant-specific body of the enum constant denoted by the reference meal.

Two constant-specific class bodies, associated with the enum constants BREAKFAST and LUNCH, override the toString() method from the java.lang.Enum class. Note that the toString() method is not overridden in the Meal enum type, but in the anonymous classes represented by two constant-specific class bodies. The third enum constant, DINNER, relies on the toString() method inherited from the java.lang.Enum class.

Constructors, abstract methods, and static methods cannot be declared in a constant-specific class body. Instance methods declared in constant-specific class bodies are only accessible if they override methods in the enclosing enum supertype.

Example 5.26 Declaring Constant-Specific Class Bodies

Click here to view code image

// File: Day.java
public enum Day {
  MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
}

Click here to view code image

// File: Meal.java
public enum Meal {
  // Each enum constant defines a constant-specific class body
  BREAKFAST(7,30) {                                                   // (1)
    @Override
    public double mealPrice(Day day) {                                // (2)
      double breakfastPrice = 10.50;
      if (day.equals(Day.SATURDAY) || day == Day.SUNDAY)
        breakfastPrice *= 1.5;
      return breakfastPrice;
    }
    @Override
    public String toString() {                                        // (3)
      return “Breakfast”;
    }
  },                                                                  // (4)
  LUNCH(12,15) {
    @Override
    public double mealPrice(Day day) {                                // (5)
      double lunchPrice = 20.50;
      switch (day) {
        case SATURDAY: case SUNDAY:
          lunchPrice *= 2.0;
      }
      return lunchPrice;
    }
    @Override
    public String toString() {
      return “Lunch”;
    }
  },
  DINNER(19,45) {
    @Override
    public double mealPrice(Day day) {                                // (6)
      double dinnerPrice = 25.50;
      if (day.compareTo(Day.SATURDAY) >= 0 && day.compareTo(Day.SUNDAY) <= 0)
        dinnerPrice *= 2.5;
      return dinnerPrice;
    }
  };
  // Abstract method implemented in constant-specific class bodies.
  abstract double mealPrice(Day day);                                 // (7)
  // Enum constructor:
  Meal(int hh, int mm) {
    this.hh = hh;
    this.mm = mm;
  }
  // Instance fields: Time for the meal.
  private int hh;
  private int mm;
  // Instance methods:
  public int getHour() { return this.hh; }
  public int getMins() { return this.mm; }
  public String getTimeString() {                              // “hh:mm”
    return String.format(“%02d:%02d”, this.hh, this.mm);
  }
}
// File: MealPrices.java
public class MealPrices {

  public static void main(String[] args) {                               // (8)
    System.out.printf(
        “Please note that %s, %s, on %s costs $%.2f.%n”,
        Meal.BREAKFAST.name(),                                           // (9)
        Meal.BREAKFAST.getTimeString(),
        Day.MONDAY,
        Meal.BREAKFAST.mealPrice(Day.MONDAY)                             // (10)
    );
    System.out.println(“Meal prices on ” + Day.SATURDAY + ” are as follows:”);
    Meal[] meals = Meal.values();
    for (Meal meal : meals) {
      System.out.printf(
          “%s costs $%.2f.%n”, meal, meal.mealPrice(Day.SATURDAY)        // (11)
      );
    }
  }
}

Output from the program:

Click here to view code image

Please note that BREAKFAST, 07:30, on MONDAY costs $10.50.
Meal prices on SATURDAY are as follows:
Breakfast costs $15.75.
Lunch costs $41.00.
DINNER costs $63.75.

In Example 5.26, the mealPrice() method declaration at (2) uses both the equals() method and the == operator to compare enum constants for equality. The mealPrice() method declaration at (5) uses enum constants in a switch statement. Note that the case labels in the switch statement are enum constant names, without the enum type name. The mealPrice() method declaration at (6) uses the compareTo() method to compare enum constants.

The main() method at (8) in Example 5.26 demonstrates calling the mealPrice() method in the constant-specific class bodies. The mealPrice() method is called at (10) and (11). Example 5.26 also illustrates the difference between the name() and the toString() methods of the enum types. The name() method is called at (9), and the toString() method is called implicitly at (11) on Meal enum values. The name() method always prints the enum constant name exactly as it was declared. Which toString() method is executed depends on whether the toString() method from the java.lang.Enum class is overridden. Only the constant-specific class bodies of the enum constants BREAKFAST and LUNCH override this method. The output from the program confirms this to be the case.


Comments

Leave a Reply

Your email address will not be published. Required fields are marked *