Skip to main content

Overview

The Card class is a simple, immutable representation of a standard playing card. It combines a rank (card value) with a suit to form a complete card in the Blackjack game. As a Java record, it provides automatic implementations of equals, hashCode, and toString methods.

Class Structure

public record Card(Rank rank, Suit suit) {
    public Card {
        Objects.requireNonNull(rank, "rank");
        Objects.requireNonNull(suit, "suit");
    }
}

Fields

rank
Rank
required
The rank (value) of the card. Determines the card’s numeric value in Blackjack scoring. Cannot be null.Possible values: TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, JACK, QUEEN, KING, ACE
suit
Suit
required
The suit of the card. In Blackjack, suits do not affect scoring but are tracked for completeness. Cannot be null.Possible values: CLUBS, DIAMONDS, HEARTS, SPADES

Validation

The compact constructor enforces non-null constraints:
public Card {
    Objects.requireNonNull(rank, "rank");
    Objects.requireNonNull(suit, "suit");
}
Attempting to create a card with null rank or suit throws NullPointerException.

Rank Enum

The Rank enum defines card values and their Blackjack scoring:
public enum Rank {
    TWO(2), THREE(3), FOUR(4), FIVE(5), SIX(6), SEVEN(7),
    EIGHT(8), NINE(9), TEN(10),
    JACK(10), QUEEN(10), KING(10),
    ACE(11);

    private final int defaultValue;

    Rank(int defaultValue) { this.defaultValue = defaultValue; }

    public int getDefaultValue() { return defaultValue; }

    public boolean isAce() { return this == ACE; }
}

Rank Values

TWO - NINE
int
Numeric cards (2-9) have face value: 2, 3, 4, 5, 6, 7, 8, 9
TEN, JACK, QUEEN, KING
int
All face cards and tens are worth 10 points
ACE
int
Default value is 11 points. The Hand class handles automatic conversion to 1 point when necessary to avoid busting.

Rank Methods

getDefaultValue() - Returns the base Blackjack value
Rank.FIVE.getDefaultValue();  // 5
Rank.KING.getDefaultValue();  // 10
Rank.ACE.getDefaultValue();   // 11
isAce() - Checks if the rank is an Ace
Rank.ACE.isAce();   // true
Rank.KING.isAce();  // false
This method is used by the Hand class to implement flexible Ace scoring (11 or 1).

Suit Enum

The Suit enum defines the four standard playing card suits:
public enum Suit {
    CLUBS, DIAMONDS, HEARTS, SPADES
}

Suit Values

CLUBS
Suit
♣ Clubs
DIAMONDS
Suit
♦ Diamonds
HEARTS
Suit
♥ Hearts
SPADES
Suit
♠ Spades
Note: In Blackjack, suits have no impact on scoring or gameplay. They are included for completeness and potential future features (e.g., displaying card images).

Usage Examples

Creating Cards

// Create specific cards
Card aceOfSpades = new Card(Rank.ACE, Suit.SPADES);
Card kingOfHearts = new Card(Rank.KING, Suit.HEARTS);
Card fiveOfClubs = new Card(Rank.FIVE, Suit.CLUBS);

// Access card properties
System.out.println(aceOfSpades.rank());  // ACE
System.out.println(aceOfSpades.suit());  // SPADES

Getting Card Values

Card card = new Card(Rank.QUEEN, Suit.DIAMONDS);
int value = card.rank().getDefaultValue();
System.out.println(value); // 10

Card ace = new Card(Rank.ACE, Suit.CLUBS);
if (ace.rank().isAce()) {
    System.out.println("This is an Ace!");
}

Card Equality

As a record, Card automatically implements value-based equality:
Card card1 = new Card(Rank.SEVEN, Suit.HEARTS);
Card card2 = new Card(Rank.SEVEN, Suit.HEARTS);
Card card3 = new Card(Rank.SEVEN, Suit.SPADES);

card1.equals(card2); // true (same rank and suit)
card1.equals(card3); // false (different suit)

Standard Deck Generation

The Deck class uses Card to generate a standard 52-card deck:
public static Deck standardShuffled(){
    List<Card> all = new ArrayList<>();
    
    for (Suit s : Suit.values()) {
        for (Rank r : Rank.values()) {
            all.add(new Card(r, s));
        }
    }
    Collections.shuffle(all);
    
    return new Deck(all);
}
This creates all 52 combinations:
  • 4 suits × 13 ranks = 52 cards

Immutability

The Card record is fully immutable:
  • Records are implicitly final (cannot be subclassed)
  • All fields are final
  • No setter methods exist
  • Rank and Suit are enums (inherently immutable)

Benefits

  1. Thread Safety - Cards can be safely shared across threads
  2. Hashable - Can be used as Map keys or in Sets reliably
  3. Cacheable - Same cards are always equal (value semantics)
  4. No Defensive Copying - Can be freely passed around without cloning

String Representation

Records automatically generate a readable toString() method:
Card card = new Card(Rank.ACE, Suit.SPADES);
System.out.println(card); // "Card[rank=ACE, suit=SPADES]"

Usage in Hand Scoring

Cards are primarily consumed by the Hand class for scoring:
public int score() {
    int total = 0;
    int aces = 0;
    
    for (Card c: cards) {
        total += c.rank().getDefaultValue();
        if (c.rank().isAce()) aces++;
    }
    
    // Adjust Aces from 11 to 1 if needed
    while (total > 21 && aces > 0) {
        total -= 10;
        aces--;
    }
    return total;
}

Complete Example

// Create a hand of cards
Card card1 = new Card(Rank.ACE, Suit.HEARTS);
Card card2 = new Card(Rank.KING, Suit.SPADES);

// Check values
System.out.println(card1.rank().getDefaultValue()); // 11
System.out.println(card2.rank().getDefaultValue()); // 10
System.out.println(card1.rank().isAce()); // true

// This would be a Blackjack! (21 with 2 cards)
int handValue = card1.rank().getDefaultValue() + card2.rank().getDefaultValue();
System.out.println(handValue); // 21

// Cards are immutable and comparable
Card duplicate = new Card(Rank.ACE, Suit.HEARTS);
System.out.println(card1.equals(duplicate)); // true
System.out.println(card1 == duplicate); // false (different objects)

Testing Support

The simple structure makes cards easy to construct for testing:
// Create specific test scenarios
Card bustCard = new Card(Rank.KING, Suit.CLUBS);  // High value card
Card lowCard = new Card(Rank.TWO, Suit.DIAMONDS);  // Low value card
Card ace = new Card(Rank.ACE, Suit.SPADES);        // Flexible value card

// Build custom decks for unit tests
List<Card> testDeck = List.of(
    new Card(Rank.ACE, Suit.HEARTS),
    new Card(Rank.KING, Suit.SPADES),
    // ... more cards
);
Deck customDeck = Deck.fromCards(testDeck);
  • Hand - Collection of cards with scoring logic
  • Deck - Manages a collection of cards for drawing
  • Game - Uses cards through Deck and Hand
  • Rank - Enum defining card ranks and values (documented above)
  • Suit - Enum defining card suits (documented above)

Build docs developers (and LLMs) love