Now that we’ve covered the basics of classes, we can talk a bit more in depth about enumerations. As we’ve discussed, an enumeration is an object type in the Java language that is limited to an explicit set of values. The values have an order that is defined by their order of declaration in the code, and have a correspondence with a string name that is the same as their declared name in the source code.
We’ve already seen a couple of examples of enumerations used in place of static identifiers. For example:
enumWeekday{Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday}// usagesetDay(Weekday.Sunday);
Let’s take a look at what the Java compiler is actually generating
for the enum. It is a regular compiled Java class, in this case named
Weekday, so we can display it with the
javap command like so:
%javapWeekdaypublicfinalclassWeekdayextendsjava.lang.Enum{publicstaticfinalWeekdaySunday;publicstaticfinalWeekdayMonday;publicstaticfinalWeekdayTuesday;publicstaticfinalWeekdayWednesday;publicstaticfinalWeekdayThursday;publicstaticfinalWeekdayFriday;publicstaticfinalWeekdaySaturday;publicstaticfinalWeekday[]values();publicstaticWeekdayvalueOf(java.lang.String);}
Weekday is a subclass of the
Enum type with seven static, final,
“constant” object references corresponding to our seven enumerated values.
Each of the enumerated values is of type Weekday. The Java compiler does not let us
extend this class or create any other instances of this type. The only
instances of Weekday that will ever
exist are the seven enumerated values. This is what gives enumerations
their type safety. A method expecting a Weekday can be given one of only seven values.
Unlike a numeric constant identifier, no value other than a Weekday will work. As we saw in Chapter 4, enumerations (unlike most objects) can also
be used in switch statements with all
the same benefits.
Because enumerations are static values, they can be imported with the Java static import, saving us some typing:
importstaticmypackage.Weekday.*;...setDay(Friday);setDeadline(Sunday);
We should also mention that enumerations can be declared not only at the “top level” alongside classes, but within classes or interfaces as well. In this case, they act just like inner classes (see Chapter 6).
You can get the ordered list of enum values for a type
with the static values() method.
Weekday[]weekdays=Weekday.values();
The compareTo() method of
an enum compares an enum value to another value of the same enum type
and returns an integer less than zero, zero, or greater than zero,
indicating whether the target enum is “less than,” “equal to,” or
“greater than” the order of the reference enum. This doesn’t mean much
for our Weekdays, but it might be
useful for values that have a more numeric meaning or a (noncyclic)
scale of some kind. For example:
Levellevel=Level.LOW;LevelanotherLevel=Level.HIGH;if(level.compareTo(anotherLevel)>0)// truedoSomething();
We mentioned that enum values have a string correspondence for
their names. You can get the string name of the value (which is exactly
the same as it is declared in the source code) with the name() method. Going
the other direction, you can “look up” any enum value by its class type
and string name using the static Enum.valueOf()
method:
StringmondayString=Weekday.Monday.name();// "Monday"WeekdaymondayWeekday=Enum.valueOf(Weekday.class,"Monday");
The name() value is also used
by the toString() method of the
value, so printing an enum value does what you’d expect.
We said that the java.lang.Enum type
cannot be directly extended and that you can’t create new instances of
enum types. However, you can add things to the generated enumeration
class when it’s declared. For example, the enumeration java.util.concurrent.TimeUnit, which has
identifiers for time units such as SECONDS, MILLISECONDS, and
MICROSECONDS, has a sleep() method
that interprets its argument in the correct time scale:
importstaticjava.util.concurrent.TimeUnit.*;SECONDS.sleep(5);// sleep 5 seconds
Enumerations can have values with constructors, methods, and fields just like other classes. For the most part, this is straightforward; you just add a semicolon after the enum values and then add your additional class members. Let’s add a “fun” value and accessor method to our weekdays:
publicenumWeekday{Sunday(8),Monday(0),Tuesday(1),Wednesday(2),Thursday(4),Friday(6),Saturday(10);intfun;Weekday(intfun){this.fun=fun;}publicintgetFun(){returnfun;}}
Here, we’ve added an instance variable, fun, to the Weekday class, as well as a constructor and
accessor method that work with the value. The declaration of our enum
values each now accepts the constructor value, much like a constructor
call without the new keyword. Note
that the semicolon at the end of the values is mandatory. Each Weekday now has a fun attribute.
There is an odd special feature of enums that we didn’t show. In
addition to adding features to the enum class as a whole (as in our
example), we can add methods and variables to individual values of the
enumeration by giving them a body with curly braces ({}). This is best served by an example:
enumCat{Himilayan,Siamese,Caleco,Persian{publicvoidsomeMethod(){...}}}
Now, only the Cat.Persian enum
value has the method. In this case, the compiler generates a subclass of
Cat as an inner class of the Persian type to hold the extra member. (We’ll
talk about inner classes in Chapter 6.) You
could use this to have the Persian
member override a method in the base enum class.