Asdfasf

Saturday, August 23, 2014

EJ-30 Use Enum instead of Int Constants

Ref: Effective Java by Joshua Bloch

// 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;
view raw IntEnumPattern hosted with ❤ by GitHub


Here is how it looks in its simplest enum form.
public enum Apple { FUJI, PIPPIN, GRANNY_SMITH }
public enum Orange { NAVEL, TEMPLE, BLOOD }
view raw SimpleEnum.java hosted with ❤ by GitHub


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. Enum types are effectively final, by virtue of having no accessible constructors. They are generalization of singleton.

Let's go on with richer enum forms with methods and fields. For a nice example of a rich enum type, consider the eight planets of our solar system. Each planet has a mass and radius, and from these two attributes youcan compute its surface gravitiy.

// Enum type with data and behavior
public enum Planet {
MERCURY(3.302e+23, 2.439e6),
VENUS (4.869e+24, 6.052e6),
EARTH (5.975e+24, 6.378e6),
MARS (6.419e+23, 3.393e6),
JUPITER(1.899e+27, 7.149e7),
SATURN (5.685e+26, 6.027e7),
URANUS (8.683e+25, 2.556e7),
NEPTUNE(1.024e+26, 2.477e7);
private final double mass; // In kilograms
private final double radius; // In meters
private final double surfaceGravity; // In m / s^2
// Universal gravitational constant in m^3 / kg s^2
private static final double G = 6.67300E-11;
// Constructor
Planet(double mass, double radius) {
this.mass = mass;
this.radius = radius;
surfaceGravity = G * mass / (radius * radius);
}
public double mass() { return mass; }
public double radius() { return radius; }
public double surfaceGravity() { return surfaceGravity; }
public double surfaceWeight(double mass) {
return mass * surfaceGravity; // F = ma
}
}
view raw PlanetEnum.java hosted with ❤ by GitHub


Enums are by their nature immutable, so all fields should be final.

While the Planet enum is simple, it is surprisingly powerful. Note that Planet, like all enums, has a static values method that returns an array o fits values in the order they were declared. Note also that the toString method returns the declared name of each enum value.

public class WeightTable {
public static void main(String[] args) {
double earthWeight = Double.parseDouble(args[0]);
double mass = earthWeight / Planet.EARTH.surfaceGravity();
for (Planet p : Planet.values())
System.out.printf("Weight on %s is %f%n",
p, p.surfaceWeight(mass));
}
}


The technique demonstrated in the Planet example are sufficient for most enum types, but sometimes you need to associate fundamentally different behavior with each constant. One way to achieve this is to switch on the value of the enum.

// Enum type that switches on its own value - questionable
public enum Operation {
PLUS, MINUS, TIMES, DIVIDE;
// Do the arithmetic op represented by this constant
double apply(double x, double y) {
switch(this) {
case PLUS: return x + y;
case MINUS: return x - y;
case TIMES: return x * y;
case DIVIDE: return x / y;
}
throw new AssertionError("Unknown op: " + this);
}
}


This code works but it isn't very pretty. Consider below powerful side of Enum, implementing abstract method in each Enum instance

// Enum type with constant-specific method implementations
public enum Operation {
PLUS { double apply(double x, double y){return x + y;} },
MINUS { double apply(double x, double y){return x - y;} },
TIMES { double apply(double x, double y){return x * y;} },
DIVIDE { double apply(double x, double y){return x / y;} };
abstract double apply(double x, double y);
}


If you add a new constant to the second version of Operation, it is unlikely that you'll forget t oprovide an apply method, as the method immediately follows each constant decleration.

So when should you use enums? Anytime you need a fixed set of constants. Of course, this includes "natural enumerated types" such as planets, the days of the week, and the chess pieces, but it also includes other sets for which you know all the possible values at compile time such as choices on a menu, operation codes etc.

No comments: