3.1 Creating an Enum
Creating an enumerated type involves three basic components, at a minimum:
- The enum keywordA name for the new typeA list of allowed values for the type
There are several optional components that may be defined as well:
- An interface or set of interfaces that the enum implementsVariable definitionsMethod definitionsValue-specific class bodies
These optional components are detailed in the labs throughout this chapter;
this lab covers the most basic concepts of enumerated types.
3.1.1 How do I do that?
Example 3-1
is about as basic of an enum as you'll find, representing a
simple Grade object.
Example 3-1. A simple enumerated type
NOTEEnums allow you
package com.oreilly.tiger.ch03;
public enum Grade { A, B, C, D, F, INCOMPLETE };
to dump most of
your "public static
final" variable
declarations.You can then define a class that refers to this enum just as it would to
any other Java class or interface, as shown in Example 3-2.NOTEMore often than
not, you'll only
need the basic
enum
functionality.NOTEThe convention is
to use all capital
letters for
enumerated type
identifiers.NOTE"Grade" is used
just like anyother
Java type.
Example 3-2. Referring to an enum in another class
Pretty basic, isn't it? The final piece is actually using this code in conjunction
package com.oreilly.tiger.ch03;
public class Student {
private String firstName;
private String lastName;
private Grade grade;
public Student(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getFirstName( ) {
return firstName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getLastName( ) {
return lastName;
}
public String getFullName( ) {
return new StringBuffer(firstName)
.append(" ")
.append(lastName)
.toString( );
}
public void assignGrade(Grade grade) {
this.grade = grade;
}
public Grade getGrade( ) {
return grade;
}
}
with the enum, as shown here:
NOTEThis code is in the
public void testGradeAssignment(PrintStream out) throws IOException {
Student student1 = new Student("Brett", "McLaughlin");
Student student2 = new Student("Ben", "Rochester");
Student student3 = new Student("Dennis", "Erwin");
student1.assignGrade(Grade.B);
student2.assignGrade(Grade.INCOMPLETE);
student3.assignGrade(Grade.A);
}
com.oreilly.
tiger.ch03.
GradeTester
class.I realize that you may have expected some complex treatment of enums,
but I'm not sure I can make it any harderenums are a nice, elegant feature
of the language, and really don't take much explainingat least for
basic usage.
3.1.2 What just happened?
I know there are many of you out there wondering about what goes on
under the hood. Here are the highlights about how you can use enums,
and their basic construction:
Enums are classes
As a result, you get type-safety,
compile-time checking, and the ability
to use them in variable declarations. This beats the proverbial
pants off of integer constants (see What about... in this
lab).
Enums extend
java.lang.Enum
java.lang.Enum is a new class in Tiger, and
is not itself an enumerated
type. All enumerated types implicitly extend Enum.
Enumerated types aren't integers
Each declared value is an
instance of the enum class itself; this
ensures type-safety and allows for even more compile-time checking.
Enums have no public constructor
This removes the ability to create additional instances of the enum
not defined at compile-time. Only
those instances defined by the
enum are available.
Enum values are
public, static,
and
final
Values cannot be overwritten, changed, or otherwise messed with in
ways that affect your programming logic. The enum itself is effectively
final, as it cannot be subclassed (see Extending an Enum).
In fact, the specification says that you are not allowed to declare an
enum as final or abstract, as the compiler will take care of those
details.
Enum values can be compared with
==
or
equals( )
Because enums are effectively final, and there is a distinct set of values,
you can usefor comparison. Additionally, enumerated types
have a working equals( ), for use in collection classes (see Maps of Enums and Sets of Enums later in this chapter).
Enums implements
java.lang.Comparable
As a result, enum values can be compared with compareTo( ), and
ordering occurs in the same order as values are declared in the enum
declaration.
Enums override
toString( )
The toString( ) method on an enumerated type returns the name of
the value. Grade.INCOMPLETE.toString( ) returns the String
"INCOMPLETE". However, this method isn't final, and can be overridden
if desired.
Enums provide a
valueOf( )
method
The static valueOf( ) method complements toString( ). Grade.
valueOf("INCOMPLETE") returns Grade.INCOMPLETE.
|
Enums define a final instance method named
ordinal( )
oridinal( ) returns the integer position of each enumerated value,
starting at zero, based on the declaration order in the enum. This
isn't a method you should use in your own code, but it's used by
other enum-related functionality, so is worth knowing about.
Enums define a
values( )
method
values( ) allows for iteration over the values of an enum, as detailed
later in this chapter in Iterating Over Enums.
NOTE"Grade" is the
type; A, B, C,
and so forth are
the values for
that type. Enum
terminologyis a bit
confusing, so it's
good to keep
these straight.NOTEEnums are not
"final" when they
have value-specific
methods,
discussed later in
the chapter.
3.1.3 What about...
...doing this in Java 1.4 (and previous releases)? At first glance, you may
not see all the advantages that enums offer, especially if you're comfortable
with static and final variables (essentially constants) in pre-Tiger
JDKs. In fact, the Grade class should look an awful lot like the OldGrade
class shown in Example 3-3, which is how you might write Grade in a pre-Tiger environment.
Example 3-3. Writing enums in pre-Tiger JDKs
However, there are a lot of problems that aren't immediately apparent.
package com.oreilly.tiger.ch03;
public class OldGrade {
public static final int A = 1;
public static final int B = 2;
public static final int C = 3;
public static final int D = 4;
public static final int F = 5;
public static final int INCOMPLETE = 6;
}
First, consider that the following line of code is legal if you are using
OldGrade:
If you move to Tiger, though, and declare that assignGrade( ) only
student1.assignGrade(1);
accepts a Grade enum, that same line will result in a compiler error:
NOTEYour error may
[javac] code\src\ch03\GradeTester.java:19:
assignGrade(com.oreilly.tiger.ch03.Grade) in
com.oreilly.tiger.ch03.Student can not be applied to (int)
[javac] student1.assignGrade(1);
look a little
differentI
formatted this to
be readable on
the page of a
book.Using the OldGrade class, you aren't passing in an object of a specific
type; you're just passing in an int, that happens (in your specific program
implementation) to be associated with the variable name OldGrade.A. You should see the enormous ability to misuse these integer constants, because they're not strongly typed to a specific grade.Even worse, consider using this same system (Student and OldGrade),
but with another "constant" class:
Now things get even hairier, because suddenly the following code is
public class OldClass {
public static final int EnglishLit = 1;
public static final int Calculus = 2;
public static final int MusicTheory = 3;
public static final int MusicPerformance = 4;
}
legal:
Some junior programmer's typo suddenly gave this student an A! Enumerated
student1.assignGrade(OldClass.EnglishList);
types may seem like a minor convenience, but they turn out to
be a major step forward for Java. Use them often, and let the compiler
catch your mistakes, rather than a late-night debugging session.