Java 1.5 Tiger A Developers Notebook [Electronic resources]

David Flanagan, Brett McLaughlin

نسخه متنی -صفحه : 131/ 37
نمايش فراداده

3.1 Creating an Enum

Creating an enumerated type involves three basic components, at a minimum:

    The enum keyword

    A name for the new type

    A 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 implements

      Variable definitions

      Method definitions

      Value-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
      package com.oreilly.tiger.ch03;
      public enum Grade { A, B, C, D, F, INCOMPLETE };

      NOTE

      Enums allow you 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.

      NOTE

      More often than not, you'll only need the basic enum functionality.

      NOTE

      The 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
      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;
      }
      }            
      

      Pretty basic, isn't it? The final piece is actually using this code in conjunction with the enum, as shown here:

      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);
      }
      

      NOTE

      This code is in the 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.

      If you change the behavior of toString( ), you need to also change the behavior of valueOf( ). These two methods should always be mirror images of each other.

      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.

      NOTE

      Enums 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
      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;
      }
      

      However, there are a lot of problems that aren't immediately apparent. First, consider that the following line of code is legal if you are using OldGrade:

      student1.assignGrade(1);
      

      If you move to Tiger, though, and declare that assignGrade( ) only accepts a Grade enum, that same line will result in a compiler error:

      [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);
      

      NOTE

      Your error may 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:

      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;
      }
      

      Now things get even hairier, because suddenly the following code is legal:

      student1.assignGrade(OldClass.EnglishList);
      

      Some junior programmer's typo suddenly gave this student an A! Enumerated 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.