Solution for Programming Exercise 5.6
This page contains a sample solution to one of the exercises from Introduction to Programming Using Java.
Exercise 5.6:
Subsection 5.7.6 discusses the possibility of representing the suits and values of playing cards as enumerated types. Rewrite the Card class from Subsection 5.4.2 to use these enumerated types. Test your class with a program that prints out the 52 possible playing cards. Suggestions: You can modify the source code file Card.java, but you should leave out support for Jokers. In your main program, use nested for loops to generated cards of all possible suits and values; the for loops will be "for-each" loops of the type discussed in Subsection 3.4.4. It would be nice to add a toString() method to the Suit class from Subsection 5.7.6, so that a suit prints out as "Spades" or "Hearts" instead of "SPADES" or "HEARTS".
CardValue is defined in Subsection 5.7.6, and is repeated below just as given there. I've added a toString() method to the definition of Suit from the same section. Given the two enumerated types, we can now represent the suit and the value of a playing card using values of type Suit and CardValue, instead of coding the suit and value as integers. In the Card class, the int variables suit and value should be replaced with variables of type Suit and CardValue. The named constants such as HEARTS and ACE in the Card class, which were used to represent suits and card values, should be deleted; they have been replaced by the constants from the enumerated types. The original Card class included instance methods getSuitAsString() and getValueAsString() to convert the suit and value of a card into an appropriate string representation. These methods are no longer necessary, since each enumerated types has its own toString() method that will automatically be used to do the conversion to string.
Class Card becomes much shorter in this version. (Most of the missing code has been moved into the enumerated types.) We just need the two instance variables to represent the suit and value of a card, getter methods to return the values of these two methods, a toString() method to do the Card-to-String conversion, and a constructor that specifies the suit and value of the card that is being constructed:
public class Card { Suit suit; // The suit of this card. CardValue value; // The value of this card. public Card( Suit theSuit, CardValue theValue ) { // Constructor. suit = theSuit; value = theValue; } public Suit getSuit() { return suit; } public CardValue getValue() { return value; } public String toString() { return value + " of " + suit; // A string such as "Ace of Spades" } }
I've made just one addition (besides comments) in the version given below. Since enumerated type values are objects, the value of a variable of type Suit or CardValue can be null. However, it doesn't make sense for the suit or value of a Card to be null. As the class stands, there is nothing to stop someone from passing null as the value of one of the actual parameters in the constructor, and that would result in an invalid Card object. To prevent this, I check the parameters in the constructor and throw a NullPointerException if an actual parameter is null.
The main program simply constructs each of the 52 possible cards, and prints out each card. The algorithm uses "for-each" loops to loop through all the possible suits and all the possible card values:
for each Suit: for each CardValue: Let c be a new Card with given suit and value print c
Using the for loop syntax from Subsection 3.4.4, a loop such as "for each Suit" becomes "for (Suit suit : Suit.values())", and this leads to the main() routine shown below.
The Main Program: /** * This program tests the Card class by creating and printing * out each possible card. */ public class TestCardEnums { public static void main(String[] args) { System.out.println("The 52 cards are:\n"); for ( Suit suit : Suit.values() ) for ( CardValue value : CardValue.values() ) { Card c = new Card(value, suit); System.out.println(" " + c); } } } The Enumerated Type "Suit" /** * A value of type Suit represents one of the four possible * suits of an ordinary playing card. */ public enum Suit { SPADES, HEARTS, DIAMONDS, CLUBS; public String toString() { switch (this) { case SPADES: return "Spades"; case HEARTS: return "Hearts"; case DIAMONDS: return "Diamonds"; default: return "Clubs"; // (CLUBS is the only other possibility.) } } } // end enum Suit The Enumerated Type CardValue: /** * Values of type CardValue represent the 13 possible values of * an ordinary playing card. */ public enum CardValue { ACE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, JACK, QUEEN, KING; /** * Return the value of this CardValue in the game of Blackjack. * Note that the value returned for an ace is 1. */ public int blackJackValue() { if (this == JACK || this == QUEEN || this == KING) return 10; else return 1 + ordinal(); } /** * Return a String representation of this CardValue, using numbers * for the numerical cards and names for the ace and face cards. */ public String toString() { switch (this) { // "this" is one of the enumerated type values case ACE: // ordinal number of ACE return "Ace"; case JACK: // ordinal number of JACK return "Jack"; case QUEEN: // ordinal number of QUEEN return "Queen"; case KING: // ordinal number of KING return "King"; default: // it's a numeric card value int numericValue = 1 + ordinal(); return "" + numericValue; } } } // end enum CardValue The Class "Card": /** * An object of type Card represents a playing card from a standard Poker * deck without the Joker. The card has a suit, which can be one of the enumerated * type values from enum Suit. A card has one of the 13 enumerated type values * from enum CardValue. Note that "ace" is considered to be the smallest value. */ public class Card { /** * This card's suit. The suit cannot be changed after the card is * constructed. */ private final Suit suit; /** * The card's value. The value cannot be changed after the card * is constructed. */ private final CardValue value; /** * Creates a card with a specified suit and value. * @param theValue the non-null value of the new card. * @param theSuit the non-null suit of the new card. * @throws NullPointerException if a parameter value is null. */ public Card(CardValue theValue, Suit theSuit) { if (theValue == null || theSuit == null) throw new NullPointerException("Card suit and value cannot be null."); value = theValue; suit = theSuit; } /** * Returns the suit of this card. */ public Suit getSuit() { return suit; } /** * Returns the value of this card. */ public CardValue getValue() { return value; } /** * Returns a string representation of this card, including both * its suit and its value. Sample return values are: * "Queen of Hearts", "10 of Diamonds", "Ace of Spades". */ public String toString() { return value + " of " + suit; } } // end class Card