Enumerated Types





Enumerated Types

A common idiom in programming is to have finite set of symbolic names that represent the values of an attribute. For example, to represent the suits of playing cards you might create a set of symbols: SPADES, HEARTS, CLUBS, and DIAMONDS. This is often called an enumerated type.

Note – Do not get confused by the term enumerated type and the java.util.Enumeration class. They are unrelated in all respects.

Old-Style Enumerated Type Idiom

Code 1 illustrates the old-style (before J2SE version 5.0) idiom for creating an enumerated type using integer constants for the symbolic names of the enumerated values.

Code 1 Old-Style Enumerated Type Example

package cards.domain;

public class PlayingCard {

// pseudo enumerated type

public static final int SUIT_SPADES = 0;

public static final int SUIT_HEARTS = 1;

public static final int SUIT_CLUBS = 2;

public static final int SUIT_DIAMONDS = 3;

private int suit;

private int rank;

public PlayingCard(int suit, int rank) {

this.suit = suit;
this.rank = rank;
}

public int getSuit() {

return suit;

}

Note – The rank attribute is also a good candidate for an enumerated type, but this code does not use an enumerated type for brevity.

Furthermore, the user interface will need to display strings rather than integers to represent the card suit. Code 2 shows the getSuitName method of the PlayingCard class. Notice Lines 37 and 38; this code is used when the suit attribute holds an invalid integer value.

Code 2 The getSuitName Method

public String getSuitName() {

String name = ““;

switch ( suit ) {

case SUIT_SPADES:

name = “Spades”;

break;

case SUIT_HEARTS:

name = “Hearts”;

break;

case SUIT_CLUBS:

name = “Clubs”;

break;

case SUIT_DIAMONDS:

name = “Diamonds”;

break;

default:

System.err.println(“Invalid suit.”);

}

return name;
}

Code 3 shows the TestPlayingCard program that creates two playing card objects and then displays their rank and suit. This program illustrates the primary problem with the old-style of enumerated type idiom. The PlayingCard constructor takes two arguments: an integer for the suit and

an integer for the rank. The first call to the PlayingCard constructor (Line 9) uses an appropriate symbolic constant, but the second call to the

PlayingCard constructor (Line 14) uses an arbitrary integer value. Both constructor calls are valid from the perspective of the compiler.

Code 3 The TestPlayingCard Program

1 package cards.tests;

2

3 import cards.domain.PlayingCard;

4

5 public class TestPlayingCard {

6 public static void main(String[] args) {

7

8 PlayingCard card1

9 = new PlayingCard(PlayingCard.SUIT_SPADES, 2);

10 System.out.println(“card1 is the “ + card1.getRank()

11 + “ of “ + card1.getSuitName());

12

13 // You can create a playing card with a bogus suit.

14 PlayingCard card2 = new PlayingCard(47, 2);

15 System.out.println(“card2 is the “ + card2.getRank()

16 + “ of “ + card2.getSuitName());

17 }

18 }

19

However, when this program is executed you can see that the second card object is invalid and does not display properly:

> java cards.tests.TestPlayingCard
card1 is the 2 of Spades
Invalid suit.
card2 is the 2 of

The statement Invalid suit. is the error message sent by the getSuitName method in Code 2.

This enumerated type idiom has several problems:

Not type-safe – Because a suit attribute is just an int, you can pass in any other int value where a suit is required. A programmer could also apply arithmetic operations on two suits, which makes no sense.

No namespace – You must prefix constants of an int enum with a string (in this case, SUIT_) to avoid collisions with other int enum types. For example, if you used an int enum for the rank value, then you would probably create a set of RANK_XYZ int constants.

Brittle character – Because int enums are compile-time constants, they are compiled into clients that use them. If a new constant is added between two existing constants or the order is changed, clients must be recompiled. If they are not, they will still run, but their behavior will be undefined.




Uninformative printed values – Because they are just int values, if you print one out all you see is a number, which tells you nothing about what it represents, or even what type it is. That is why the PlayingCard class needs the getSuitName method.

The New Enumerated Type

The Java SE version 5.0 of the Java programming language includes a type-safe enumerated type feature. Code 4 shows an enumerated type for the suits of a playing card. You can think of the Suit type as a class with a finite set of values that are given the symbolic names listed in the type definition. For example, Suit.SPADES is of type Suit.

Code 4 The Suit Enumerated Type

1 package cards.domain;

2

3 public enum Suit {

4 SPADES,

5 HEARTS,

6 CLUBS,

7 DIAMONDS

8 }

Code 5 shows the PlayingCard class using the Suit type for the data type of the suit attribute.

Code 5 The PlayingCard Class Using the Suit Type

1 package cards.domain;

2

3 public class PlayingCard {

4

5 private Suit suit;

6 private int rank;

7

8 public PlayingCard(Suit suit, int rank) {

9 this.suit = suit;

10 this.rank = rank;

11 }

12

13 public Suit getSuit() {

14 return suit;

15 }

16 public String getSuitName() {

17 String name = ““;

18 switch ( suit ) {

19 case SPADES:

20 name = “Spades”;

21 break;

22 case HEARTS:

23 name = “Hearts”;

24 break;

25 case CLUBS:

26 name = “Clubs”;

27 break;

28 case DIAMONDS:

29 name = “Diamonds”;

30 break;

31 default:

32 // No need for error checking as the Suit

33 // enum is finite.

34 }

35 return name;

36 }

This solves the type-safety issues with the old-style enumerated type idiom. Code 6 shows an updated test program. Line 14, if uncommented, would result in a compiler error because the int value of 47 is not of type Suit.

Code 6 The TestPlayingCard Program

1 package cards.tests;

2

3 import cards.domain.PlayingCard;

4 import cards.domain.Suit;

5

6 public class TestPlayingCard {

7 public static void main(String[] args) {

8

9 PlayingCard card1

10 = new PlayingCard(Suit.SPADES, 2);

11 System.out.println(“card1 is the “ +

card1.getRank()

12 + “ of “ + card1.getSuitName());

13

14 // PlayingCard card2 = new PlayingCard(47, 2);

15 // This will not compile.

16 }

17 }




Advanced Enumerated Types

Unfortunately, the PlayingCard class still requires a getSuitName method to display a user-friendly name in the user interface. If the program were to display the actual Suit value, it would show the symbolic name of the type value; for example, Suit.SPADES would display as SPADES. This is more user-friendly than “0,” but is still not as user-friendly as Spades.

Furthermore, the name of the suit should not be part of the PlayingCard class, but rather part of the Suit type. The new enumerated type feature permits both attributes and methods, just like regular classes. Code 7 shows a refinement of the first Suit type (Code 7-4 on page 7-13) with a name attribute and getName method. Notice the use of proper information hiding with the private attribute and the public accesser method.

Code 7 The Suit Type with an Attribute

1 package cards.domain;

2

3 public enum Suit {

4 SPADES (“Spades”),

5 HEARTS (“Hearts”),

6 CLUBS (“Clubs”),

7 DIAMONDS (“Diamonds”);

8

9 private final String name;

10

11 private Suit(String name) {

12 this.name = name;

13 }

14

15 public String getName() {

16 return name;

17 }

18 }

An enum constructor should always use the private accessibility. The arguments to the constructor are supplied after each declared value. For example, on Line 4 the string “Spades” is the argument to the enum constructor for the SPADES value. Enumerated types can have any number of attributes and methods.

One thought on “Enumerated Types

  1. Wonderful work! This is the type of info that should be shared around the web. Shame on Google for not positioning this post higher! Come on over and visit my site . Thanks =)

Comments are closed.