Effective Java 30 Use Enums instead of int constants

时间:2022-07-16 03:21:09

Enumerated type is a type whose legal values consist of a fixed set of constants, such as the seasons of the year.

// The int enum pattern - severely deficient!

public static final int APPLE_FUJI = 0;

public static final int APPLE_PIPPIN = 1;

public static final int APPLE_GRANNY_SMITH = 2;

public static final int ORANGE_NAVEL = 0;

public static final int ORANGE_TEMPLE = 1;

public static final int ORANGE_BLOOD = 2;

The basic idea behind Java's enum types is simple: they are classes that export one instance for each enumeration constant via a public static final field.

To associate data with enum constants, declare instance fields and write a constructor that takes the data and stores it in the fields.

Note

  1. unless you have a compelling reason to expose an enum method to its clients, declare it private or, if need be, package-private (Item 13).
  2. If an enum is generally useful, it should be a top-level class; if its use is tied to

    a specific top-level class, it should be a member class of that top-level class (Item22)

  3. Compiler will reminds you that abstract methods in an enum type must be overridden with concrete methods in all of its constants.

    /**

    * Demo for enum type use abstract methods for Compile time checking on added enum type.

    */

    package com.effectivejava.EnumAnnotations;

    import java.util.HashMap;

    import java.util.Map;

    /**

    * @author Kaibo

    *

    */

    public enum Operation {

    PLUS("+") {

    public double apply(double x, double y) {

    return x + y;

    }

    },

    MINUS("-") {

    public double apply(double x, double y) {

    return x - y;

    }

    },

    TIMES("*") {

    public double apply(double x, double y) {

    return x * y;

    }

    },

    DIVIDE("/") {

    public double apply(double x, double y) {

    return x / y;

    }

    };

    private final String symbol;

    Operation(String symbol) {

    this.symbol = symbol;

    }

    @Override

    public String toString() {

    return symbol;

    }

    public abstract double apply(double x, double y);

    }

    Note

    Enum types have an automatically generated valueOf(String)method that translates a constant's name into the constant itself. If you override the toString method in an enum type, consider writing a fromString method to translate the custom string representation back to the corresponding enum.

    // Implementing a fromString method on an enum type

    private static final Map<String, Operation> stringToEnum = new HashMap<String, Operation>();

    static { // Initialize map from constant name to enum constant

    for (Operation op : values())

    stringToEnum.put(op.toString(), op);

    }

// Returns Operation for string, or null if string is invalid

public static Operation fromString(String symbol) {

return stringToEnum.get(symbol);

}

4. Use nested enum for the strategy enum pattern. Using constant-specific methods.

/**

* Demo for the "Using Nested enum for enum strategy pattern."

*/

package com.effectivejava.EnumAnnotations;

/**

* @author Kaibo

*

*/

// The strategy enum pattern

enum PayrollDay {

MONDAY(PayType.WEEKDAY), TUESDAY(PayType.WEEKDAY), WEDNESDAY(

PayType.WEEKDAY), THURSDAY(PayType.WEEKDAY), FRIDAY(PayType.WEEKDAY), SATURDAY(

PayType.WEEKEND), SUNDAY(PayType.WEEKEND);

private final PayType payType;

PayrollDay(PayType payType) {

this.payType = payType;

}

double pay(double hoursWorked, double payRate) {

return payType.pay(hoursWorked, payRate);

}

// The strategy enum type

private enum PayType {

// constant-specific methods

WEEKDAY {

double overtimePay(double hours, double payRate) {

return hours <= HOURS_PER_SHIFT ? 0 : (hours - HOURS_PER_SHIFT)

* payRate / 2;

}

},

WEEKEND {

double overtimePay(double hours, double payRate) {

return hours * payRate / 2;

}

};

private static final int HOURS_PER_SHIFT = 8;

abstract double overtimePay(double hrs, double payRate);

double pay(double hoursWorked, double payRate) {

double basePay = hoursWorked * payRate;

return basePay + overtimePay(hoursWorked, payRate);

}

}

}

5. Switches on enums are good for augmenting external enum types with constant-specific behavior.

// Switch on an enum to simulate a missing method

public static Operation inverse(Operation op) {

switch(op) {

case PLUS: return Operation.MINUS;

case MINUS: return Operation.PLUS;

case TIMES: return Operation.DIVIDE;

case DIVIDE: return Operation.TIMES;

default: throw new AssertionError("Unknown op: " + op);

}

}

Summary

Enums are far more readable, safer, and more powerful than int constants. Enums can benefit from associating data with each constant sand providing methods whose behavior is affected by this data. When enums benefit from assocating multiple behaviors with a single method perfer constant-specific methods to enums that switch on their own values. Consider the strategy enum pattern if multiple enum constants share common behaviors.