Non-Static Inner Classes
An inner class is a class defined inside another class, and its objects are always associated with an object of the containing class.
Why an Inner Object Cannot Stand Alone
A class can be defined inside another class. Such a class is called an inner class. Once you have one, a practical question follows immediately: if the inner class lives inside another class, how does code outside that containing class name it, and how does it create an object of it? The answer is not the same as for an ordinary top-level class, and the difference reveals something important about what an inner class object is connected to.
Keep this question in mind as you read. The rest of this article follows a small poker program through three moments: when an inner object is created inside its containing class, when it is created from outside, and when creation is not possible at all. Before you read each explanation, try to predict where the program will need an existing object and where it will not.
Tracing a Poker Program Step by Step
The source uses a PokerGame class with a nested Player class. The PokerGame class has instance variables named deck and pot. Here is a simplified version of that structure. It is written for illustration and follows the relationships the source describes.
public class PokerGame {
private Deck deck;
private int pot;
public PokerGame() {
deck = new Deck();
pot = 0;
}
public void seatPlayer() {
Player p = new Player();
p.joinTable();
}
class Player {
void joinTable() {
System.out.println("Joined a game with pot " + pot);
}
}
}- 1The PokerGame constructor gives this game its own deck and sets its own pot to zero.
- 2The
seatPlayermethod is an instance method. It runs on a particular PokerGame object. - 3The line new
Player()creates an inner object without naming a game. The game it belongs to is the object whose method is running. - 4Inside
joinTable, the Player object reads pot. That value is the pot of the game the player belongs to, not a shared value.
- Deck and pot are instance variables that belong to one particular PokerGame object.
- Player is defined inside PokerGame, so it is an inner class.
- Inside
seatPlayer, the expression newPlayer()appears with no variable in front of it.
What do you think happens?
Inside an instance method of PokerGame, does new Player() need a game variable in front of it?
Reveal answer
No, the containing object is supplied implicitly
Inside the containing class, the object this is used implicitly, so a plain new expression is enough. The explicit form is needed only when the creating code sits outside the containing class.
The Containing Object as Context
An inner class is a class defined inside another class, and its objects are always associated with an object of the containing class.
The key rule is this: to create an object that belongs to an inner class, you must first have an object that belongs to the containing class. The inner class object is never created on its own. It is always created in connection with a particular containing object. You can think of the containing object as the context the inner object is built within.
The diagram shows the association the source describes. A Player object does not float free. It reaches the deck and pot through the game it belongs to, and that link is what makes it a player of one specific game. Each PokerGame object has its own deck and its own pot. Players of one game use that game's deck and pot, and players of another game use the other game's deck and pot.
Because the Player is tied to one container, its code can use the instance variables of that container. A non-static inner object can access the instance variables of its particular containing object, which is why each game's Player works with that game's own deck and pot.
Naming an Inner Class from Outside
Code outside the containing class has no implicit container, so it must name one. From outside the containing class, an inner class is referred to with a name of the form variableName dot NestedClassName. The variableName part is a variable that refers to the containing object that holds the inner class. The left side names the container, and the right side names the inner class within it.
Creation follows the same pattern. Where a variable named game refers to a PokerGame object, an inner Player object can be created from outside with the expression game.new Player(). The word new is attached to the containing variable, which is how the expression tells Java which containing object the new Player belongs to. The source describes creating an inner object outside the containing class as rare, but the syntax is the one to recognize.
Creating a Player for a Specific Game
Two PokerGame objects exist: a variable named friendlyGame refers to one, and a variable named tournament refers to another. Write code outside PokerGame that creates one Player for each game.
- 1Identify the containers
The containing objects are the ones referred to by
friendlyGameand tournament. Each variable names a different game. - 2Attach new to each container
For the first game, write
friendlyGame.newPlayer(). The new keyword is attached to the variable that names the container. - 3Repeat for the second game
For the second game, write tournament.new
Player(). The Player created here belongs to the tournament game and uses that game's deck and pot. - 4Check the link
Swapping the variable names would attach each Player to the other game. The variable in front of new decides which game the player belongs to.
The code is friendlyGame.new Player() and tournament.new Player(). Each Player belongs to exactly one game, chosen by the variable placed before new.
When an Inner Object Can Be Created
Because the inner object always needs a containing object, the test for whether creation is possible is simple: does the creating code have a containing object to supply? Start from where the creation code runs. If the code is inside an instance method of the containing class, the containing object is this, and new Player() works. If the code is outside the containing class, it must name a containing object with a variable and write variable.new Player(). If no containing object is available at all, creation cannot succeed.
Mistakes With Containing Objects
Common mistake Writing new Player() from outside the containing class with no variable in front
What it looks likePlayer p = new Player(); placed in a separate class's main method
Why it failsOutside the containing class there is no implicit container, so the creation has no game to belong to.
Do this insteadName a containing variable: Player p = game.new Player();Common mistake Reversing the container and the inner class name
What it looks likePlayer p = Player.new game();Why it failsThe container variable goes on the left of the dot and new sits between the container and the inner class name.
Do this insteadWrite game.new Player(), with the container variable first.
Common mistake Assuming the inner object belongs to whichever game was created last
What it looks likeTwo games are made, and a Player is created with new Player() in a context that has no clear game
Why it failsThe inner object is tied to the specific container it was created with, and a missing or wrong container variable attaches it to the wrong game.
Do this insteadWalk through each creation expression and ask which containing object it belongs to.
When an inner class program fails to compile or behaves unexpectedly, the most useful question is where the inner object lost its container. A missing or wrong containing variable is the most common reason an inner object ends up attached to the wrong game, or cannot be created at all.
Predict and Check Your Reasoning
Suppose a PokerGame variable named game refers to a real game, and a second variable named rival refers to another real game. Write the expression that creates a Player belonging to rival, then state what the Player can read from its game: the deck and pot of the game named game, or those of the game named rival?
Hints
- Find the variable that names the container, then place it before new.
- A Player reads the deck and pot of the container it was created with.
A method in an unrelated class has no PokerGame variable available to it. Predict whether it can write Player p = new Player(); and explain which piece of information is missing.
Hints
- Ask what containing object the new expression would belong to.
- Compare this situation with the one inside
seatPlayer.
Key Takeaways
- An inner class is a class defined inside another class, and each of its objects is always associated with an object of the containing class.
- Inside an instance method of the containing class, the containing object is implicit, so new
Player()is enough. - From outside the containing class, name the container with a variable and write variable.new
NestedClassName(), such as game.newPlayer(). - A non-static inner object can use the instance variables of its particular containing object, so each game's players use that game's deck and pot.
- If no containing object is available, creation of the inner object cannot succeed, and the fix is to supply the correct container.