Java access modifiers: examples from a game
By: Alonso Del Arte
Tags: Java, Blackjack, Encapsulation, Object Oriented, Access Modifier

Photo by Markus Spiske on Unsplash

Using different combinations of access modifiers, Java programmers can create a system of very fine-grained access control in their Java projects. This is a good thing, it simplifies programming.

Access modifiers enable programmers to declare their intentions regarding encapsulation, and then to offload enforcement of those intentions to the computer, which is almost always better than the human mind at keeping track of such things.

If you’ve studied Java even just a little bit beyond Hello World, you have probably memorized the access modifiers and how they differ. Here’s a very quick refresher:

Public ( public ) means accessible from anywhere, subject to caveats for modules (Java 9 and later).

) means accessible from anywhere, subject to caveats for modules (Java 9 and later). Protected ( protected ) means accessible from any subclasses and from classes and interfaces in the same package.

) means accessible from any subclasses and from classes and interfaces in the same package. Package private (tacit access modifier in classes) means accessible only from classes and interfaces in the same package.

Class private ( private ) means accessible only in the defining class (starting with Java 9, there is also interface private with the same access modifier as class private).

Enumerated types are essentially special classes with special syntax. Not that different from regular classes.

I wrote an article delving into these in great detail, but only using toy examples with no obvious applicability to actual programs.

With toy examples, an author can shut out distracting extraneous details. But toy examples can also give the impression that a particular concept has no practical use whatsoever.

So I thought I should use something serious and well-known as an example, like checking and savings accounts. Then again, any example has the potential to feel unrealistic.

And examples from something seemingly less serious, like a game, can avoid the pitfalls of toy examples by being an actual implementation of something that is fairly well-known, making it easier for readers to see the applicability of the concept being illustrated to their own actual projects.

For example, blackjack. Even if you don’t know how to play blackjack, you probably know that it involves some cards face up and some cards face down. So I don’t have to explain that.

I’ve been working on a blackjack implementation in Java (source and tests are on GitHub). It’ll eventually have a graphical user interface, but for now you can play at the command line.

It’s missing the ability to split hands and the ability to double down on wagers, and the main class needs a lot of refactoring, but I think I’ve got the basic game play down pretty well.

The thing about access modifiers is that beginning Java students often think the modifiers have to do with information security; they don’t.

If you have the source of a program, like the latest commit of my blackjack program, you can change any access modifier you want. Of course that won’t change my repository nor my local files.

The program will probably still compile and work, but such changes won’t help you cheat at the game. The access modifiers don’t have anything to do with your being able to see what the dealer’s face down card is, nor what the next card is.

Here’s an example of actual game play with my blackjack program:

BLACKJACK Enter your wager in whole dollars: $100 Your first card is Six of Spades

Dealer’s face-up card is Queen of Spades Your second card is Eight of Diamonds

Your hand’s value is 14 Dealer’s face-down card is ????? Hit or stand? hit

Your next card is King of Spades

Your hand’s value is 24 Dealer’s face-down card is Ten of Diamonds

Dealer’s hand value is 20

Dealer collects your $100

Better luck next time…

Note that all money amounts mentioned in this article are play money. If you gamble more real money than you can afford to lose, contact the appropriate public health organization.

I guess it wouldn’t have helped me any to know that the dealer had a 10♦. I couldn’t split the 6♠ and 8♦, because I haven’t implemented splits yet and because even if I did the program should not allow such a split.

I could’ve stood at 14, but if the dealer’s face down card is low (e.g., a deuce), they’re obligated to draw to reach or exceed 16, risking going bust. The only way for me to win at that point would have been to get a seven.

Actually, as I reread the rules on the Bicycle website, I think I may have made a mistake with my implementation: if I stood at 14 but the dealer went over 21, I would have won my bet, as I’m understanding the rules now.

But maybe even knowing the dealer’s face down card in this particular case I would still have asked for another card, because there’s still a chance of standing off if I get a six or winning if I get a seven, as opposed to certainly losing with my 14 to the dealer’s 20.

Here’s another round:

BLACKJACK Enter your wager in whole dollars: $100 Your first card is Five of Hearts

Dealer’s face-up card is King of Clubs Your second card is Nine of Diamonds

Your hand’s value is 14 Dealer’s face-down card is ????? Hit or stand? stand Dealer’s face-down card is Seven of Diamonds

Dealer’s hand value is 17

Dealer collects your $100

Better luck next time…

Knowing the face down card on this one would have been much more helpful this time around. The dealer must stand at 17, giving me more possibilities to win, by getting a four, a five, a six or a seven.

Here’s how the program “redacts” the dealer’s face down card:

PlayingCard faceDownCard = dispenser.getNextCard();

dealerHand.add(faceDownCard);

System.out.println("Dealer's face-down card is ?????");

System.out.println();

The indentation level suggests I need to refactor to lower the cyclomatic complexity. That’s one of those distracting extraneous details that wouldn’t occur in a toy example.

I could easily change this to

PlayingCard faceDownCard = dispenser.getNextCard();

dealerHand.add(faceDownCard);

System.out.println("Dealer's face-down card is "

+ faceDownCard.toASCIIString);

System.out.println();

Then this would be somewhat but not quite like a blackjack variant known as Zweikartenspiel (literally “two-card play”). That’s another distracting extraneous detail.

A much more helpful cheat might be a heads up as to what card is next in the shoe. Such a cheat is easily accomplished without changing any access modifiers anywhere in the project.

If you’re looking on my GitHub repository, or if you pulled it to your local machine and loaded it in your favorite integrated development environment (IDE), you can see that dispenser is an instance of MultiDeckCardDispenser from the playingcards package.

The following source listing omits all Javadoc comments without any indication of such omissions. Other omissions are indicated.

I should mention that this is a Java 8 project. If you’re working on this with Java 9 or later, all the packages of this project are placed in the default unnamed module, and I believe you don’t have to do anything special to access the java.util package. Let me know if that’s not the case.

So you can access MultiDeckCardDispenser from any package in the project, because it’s marked public. The same is true of the CardSupplier interface.

The primary MultiDeckCardDispenser constructor takes one or more decks, shuffles each separately, puts each card in the private cards array-list, then shuffles the whole collection.

To get a card, classes and interfaces in other packages are supposed to call getNextCard() , which is of course public.

Knowing just one card in advance would be a big help for cheating. So I insert four lines in getNextCard() :

@Override

public PlayingCard getNextCard() {

if (this.dispenseIndex == this.cards.size()) {

throw new RanOutOfCardsException("Ran out of cards");

}

// TODO: Remove this line and next three after demonstration

System.out.println("CHEAT: Card "

+ this.cards.get(dispenseIndex + 1).toASCIIString()

+ " is coming up soon...");

return this.cards.get(this.dispenseIndex++);

}

Be sure to explicitly save this if you don’t have auto-save turned on in your IDE (e.g., like in IntelliJ by default).

BLACKJACK Enter your wager in whole dollars: $100 CHEAT: Card Six of Spades is coming up soon…

Your first card is Ten of Spades

CHEAT: Card Queen of Spades is coming up soon…

Dealer’s face-up card is Six of Spades CHEAT: Card Ace of Diamonds is coming up soon…

Your second card is Queen of Spades

Your hand’s value is 20 CHEAT: Card Two of Hearts is coming up soon…

Dealer’s face-down card is ????? Hit or stand? stand Dealer’s face-down card is Ace of Diamonds

Dealer’s hand value is 17

As you have a higher score,

*** YOU WIN $100 ****

I feel like my luck is changing for the better. Of course standing at 20 is the smart decision in normal game play, because, with the dealer showing 6♠, it’s much likelier that the dealer will get to 21 than that you’ll get an ace as your next card.

With the cheat, I might as well wager more play money.

BLACKJACK Enter your wager in whole dollars: $1,000 CHEAT: Card Seven of Hearts is coming up soon…

Your first card is Three of Hearts

CHEAT: Card Three of Hearts is coming up soon…

Dealer’s face-up card is Seven of Hearts CHEAT: Card Two of Hearts is coming up soon…

Your second card is Three of Hearts

Your hand’s value is 6 CHEAT: Card Two of Clubs is coming up soon…

Dealer’s face-down card is ?????

Remember that the dealer shuffled multiple decks into the dispenser, so it’s perfectly normal and valid that I’ve gotten two cards that are both 3♥ one right after the other.

I undid the ersatz Zweikartenspiel change a while ago, but because of the current cheat, I know that the dealer’s face down card is actually 2♥. At just 6, it makes no sense for me to stand at this point, because the dealer’s at 9 and must get to 17 or higher.

Hit or stand? hit

CHEAT: Card Jack of Clubs is coming up soon…

Your next card is Two of Clubs

Your hand’s value is 8 Hit or stand? hit

CHEAT: Card Four of Clubs is coming up soon…

Your next card is Jack of Clubs

Your hand’s value is 18

Normally it would make sense to stand at 18 when you know the dealer has low cards. You are likelier to go bust than to get to 19, 20 or 21. But here I know for certain that I’d go bust at 22 if I take that 4♣ that’s in the pipeline.

I also know that when the dealer gets that 4♣, they will need to take at least one more card, and there’s a chance they might go bust. The dealer has, in blackjack parlance, a “stiff hand.”

Hit or stand? stand Dealer’s face-down card is Two of Hearts

Dealer’s hand value is 9

CHEAT: Card Ten of Hearts is coming up soon…

Dealer takes Four of Clubs

Dealer’s hand value is 13 CHEAT: Card Five of Clubs is coming up soon…

Dealer takes Ten of Hearts

Dealer’s hand value is 23 Since you stood and the dealer went bust,

*** YOU WIN $1,000 ****

Again, this is play money, not real money.

I found all those heads up about the upcoming cards to be rather distracting, and my game play was much slower.

If I tried to count cards in an actual casino, I doubt anyone would notice (because I wouldn’t win more than normal, if I win at all). So I’m reverting the cheat, as indicated by the TODO comment.

Too many auto-complete suggestions can also be distracting. Suppose for example that MultiDeckCardDispenser ’s fields are all public. Then, working on a class in another package using NetBeans, I might see something like this:

The first option to complete “ PlayingCard card = dispenser. ” is of course “ getNextCard(); ,” as it should be. But then there’s also the option of choosing “ cards ,” and then I’d have to choose get() .

But at what index? Maybe dispenser.dispenseIndex . And then the caller would have to either advance dispenseIndex or have its own variable to keep track of the index for the next card.

It would be a waste of my time working to make sure that getNextCard() works correctly only to let other classes access the cards array-list directly. So better to make it private.

Then, when working with classes that import MultiDeckCardDispenser , the getNextCard() function will still be at the top of suggestions to auto-complete when the type is PlayingCard , but the list of suggestions will now be limited to the three functions mandated by CardSupplier and the functions and procedures inherited from Object .

It shouldn’t matter to the caller how MultiDeckCardDispenser manages its PlayingCard instances. And with the cards array-list, decks array and deal index hidden from callers, it doesn’t matter to callers.

Clearly PlayingCard is also a public class. But its constructor is package private, so that it can only be called by classes and interfaces in the playingcards package.

Remember that in the context of a class which is not an enumerated type, no access modifier for a constructor means package private. The fields cardRank and cardSuit are of course private and final.

This means that new PlayingCard instances can only be created in the playingcards package.

I don’t expect this to reassure players that this is an honest game, that the program can’t just manufacture a bad card for the player at a crucial juncture. The program could in fact be rewritten to do something like that, without our having to change any access modifiers.

In the playingcards package, but filed under Test Packages, I have written the CardServer class, which implements the CardSupplier interface. In addition to getNextCard() , CardServer also provides a function to give a card of a specific rank, and another to give a card of a specific suit.

Even more easily, I could write

public PlayingCard giveCard(Rank rank, Suit suit) {

return new PlayingCard(rank, suit);

}

For blackjack, the previous giveCard() with just the Rank parameter would be sufficient, to give the dealer a nice low card when they might go bust, and give the player a bad high card to ensure that the player does go bust.

In the CardServer Javadoc, I wrote that “This class should only be accessible to test classes, to facilitate the testing of hand scoring.” To blackjack.Dealer in Source Packages, the only available CardSupplier implementations should be ones that disallow peeking ahead.

Like I mentioned earlier, I don’t expect this to reassure end users, but I do expect it to reassure other programmers that separate concerns have been properly separated and encapsulated.

The types Rank and Suit are enumerated types that both implement the CardSpec interface. In the source, they would seem to have package private constructors. But the compiler will write class private constructors for them, and NetBeans will acknowledge them as such.

The following screenshot shows that:

A genuinely package private constructor gets a slightly but noticeably different icon in the NetBeans Navigator:

Hmm… I might be able to use CardSpec to eliminate the redundancy of separate giveCard() functions for Rank and Suit in CardServer …

This led me to question why the CardSpec interface is public. It doesn’t need to be public. So I changed it to package private.

NetBeans scanned the project and notified me that there were compilation errors in the playingcards.matchers subpackage (which is a separate package, as far as the Java runtime is concerned).

That proves, once again, that a computer is better than the human mind at keeping track of details like these. In this case I had actually forgotten what my motivation was for writing the CardSpec interface in the first place.

But since it’s in Test Packages rather than Source Packages (which can be considered “production”), it’s not a big priority for me.

I don’t have anything with protected access in this project. I definitely used protected access in my earliest Java projects, and you can still find it on my GitHub repositories (though in almost all cases I will eventually get around to refactoring it out).

It’s not because I’m overly dogmatic about it, I acknowledge that protected access has a time and a place. It’s just that public, package private and class private tend to be sufficient for most projects.