Concepts / Non-Static Inner Classes

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.

  • Programming

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.

Code walkthrough
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);
        }
    }
}
  1. 1
    The PokerGame constructor gives this game its own deck and sets its own pot to zero.
  2. 2
    The seatPlayer method is an instance method. It runs on a particular PokerGame object.
  3. 3
    The line new Player() creates an inner object without naming a game. The game it belongs to is the object whose method is running.
  4. 4
    Inside 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 new Player() 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?

  • Yes, it must always be written as game.new Player()

  • No, the containing object is supplied implicitly

Reveal answer
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

Definition

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.

PokerGame objectone particular gamedeckthis game's deckpotthis game's potPlayer objecta player of this gamehashasbelongs to
What does each Player object hold a connection to, and how do the game and the player relate? Each Player reaches the deck and pot through the game it belongs to.

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.

gamevariable naming thecontaining PokerGame object.separates container frominner classnewcreates an inner object inthat containerPlayer()the inner class beingcreated
In the expression game.new Player(), which part names the outer instance, and which part names the inner class? Each token supplies a different piece of the creation.

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.

Worked example

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.

  1. 1
    Identify the containers

    The containing objects are the ones referred to by friendlyGame and tournament. Each variable names a different game.

  2. 2
    Attach new to each container

    For the first game, write friendlyGame.new Player(). The new keyword is attached to the variable that names the container.

  3. 3
    Repeat 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.

  4. 4
    Check 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.

Result

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.

Creation code runsInside the containingclass?Use new Player()containing object is thisUse variable.newPlayer()container named explicitlyHas a containingvariable?Creation failsno containing objectyesnoyesno
Starting from where the creation code runs, which path leads to a valid inner-object creation, and which path fails because no containing object is available?

Mistakes With Containing Objects

  • Common mistake

    Writing new Player() from outside the containing class with no variable in front

    What it looks like

    Player p = new Player(); placed in a separate class's main method

    Why it fails

    Outside the containing class there is no implicit container, so the creation has no game to belong to.

    Do this instead
    Name a containing variable: Player p = game.new Player();
  • Common mistake

    Reversing the container and the inner class name

    What it looks like
    Player p = Player.new game();
    Why it fails

    The container variable goes on the left of the dot and new sits between the container and the inner class name.

    Do this instead

    Write game.new Player(), with the container variable first.

  • Common mistake

    Assuming the inner object belongs to whichever game was created last

    What it looks like

    Two games are made, and a Player is created with new Player() in a context that has no clear game

    Why it fails

    The 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 instead

    Walk 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

EASY

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.
MEDIUM

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.new Player().
  • 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.