What are Inner Classes?

An inner class is one that is defined within another class.

Why is this good thing?  Here are some reasons:

  • An inner class can make clear an explicit relationship with the enclosing class.
  • An inner class can be hidden within the enclosing class. (Data hiding/encapsulation.)
  • Certain kinds of inner classes can access all the fields and methods within the enclosing class, which can be very helpful.

The bottom line is that using inner classes can lead to more readable, more maintainable, and more reliable code.

Nested Classes in C++

C++ does not have inner classes, per se.  It has only nested classes

What’s the difference?  C++ nested classes is a relationship between classes, as opposed to between objects.  As we’ll see, Java inner classes usually do have a relationship between objects.

Here’s an example of C++ nested classes. (Note that the implementation isn’t completely spelled out, because we don’t want to get bogged down in details that are not relevant to the topic at hand.)

#ifndef __LIST_H
#define __LIST_H
//
// LinkedList.h
//

#include <string>
using namespace std;

/////////// Class LinkedList ///////////////

class LinkedList
{
    class Link;     // Forward reference
public:
    LinkedList();   // Constructor
    ~LinkedList();  // Destructor
    // Access
    Link *GetFirst() const; // Returns ptr to first item in list
    Link *GetLast() const;  // Returns ptr to last item in list
    int GetCount() const;   // Returns count of items in list

    // Operations
    void Append(Link &item); // Appends an item to the end of the list
    void Purge();            // Removes all items from the list

    ////////// A public nested Iterator class ////////
    class Iterator
    {
    public:
        Iterator(const LinkedList &list);   // Constructor
        ~Iterator();                        // Destructor

        // Access
        const LinkedList *GetList() const; // Return ptr to the iterator's list
        Link *GetCurrent() const;    // Return ptr to current list item
        Link *GetNext();             // Move to next item and return ptr to it
        Link *GetPrevious();         // Move to previous item and return ptr to it
        bool  AtFirst() const;       // return true if current item is first item
        bool  AtLast() const;        // return true if current item is last item

    private:
        // Disable copy constructor for Iterator
        Iterator(const Iterator &);
        // Disable assignment for Iterator
        Iterator &operator=(const Iterator &);

        // Data
        const LinkedList *m_list;    // The list we are iterating through
        Link   *m_current;           // Points to the current item in the list
    };

private:
    // Disable copy constructor for LinkedList
    LinkedList(const LinkedList &);
    // Disable assignment for LinkedList
    LinkedList &operator=(const LinkedList &);

    // Data members
    Link *m_first;    // Pointer to the first list item
    Link *m_last;    // Pointer to the last list item
    int m_count;         // Count of list items currently in list

    ////////// A private nested Link class //////////////
    class Link
    {
    public:
        Link(void *data);    // Constructor (associates with data)
        ~Link();            // Destructor

        // Operations
        void Add(LinkedList &list, Link *prevItem);
        // Add this Link to the specified list
        // following the specified prevItem
        // (if prevItem is NULL, place at front of list)

        void Remove();            // Remove this list item from the list it is in

        // Access member functions
        Link *GetNext() const;     // Return ptr to next item in list
        Link *GetPrevious() const; // Return ptr to previous item in list
        const LinkedList *GetList() const;   // Return ptr to the list item's list
        void *GetData();               // Returns ptr to data for item

    private:
        // Disable copy constructor for Link
        Link(const Link &);
        // Disable assignment for Link
        Link &operator=(const Link &);

        // Data members
        Link       *m_next;    // Pointer to next item in the list
        Link       *m_prev;    // Pointer to previous item in the list
        LinkedList *m_list;    // Pointer to the list the item currently is in
        void       *m_data;    // Pointer to data
    };
};
#endif // __LIST_H

The basic benefits of nested classes in C++ are:

  • Access control — you can place a nested class in a private, public, or protected section of the enclosing class
  • Naming — if the nested class is to be used from outside of the enclosing class, its name must be qualified by the enclosing class’s name.  So, for example, if you wanted to use the above nested Iterator class from outside the LinkedList class, you would have to refer to it as follows: LinkedList::Iterator
    That is, you would have to explicitly qualify it with the enclosing class’s name.

A Stack Class

Before we talk about inner or nested classes, let’s show an example of how we might implement something without those features.

Here’s a very simple Stack class in Java:

package examples;

/**
 * Stack implements a simple stack of integers
 */
public class Stack
{
  /**
   * Creates a stack of specified number of elements
   */
  public Stack(int size)
  {
    m_stackData = new int[size];
  }
  /**
   * Pushes an integer value on the stack, 
   * or throws an exception, if insufficient space
   */
  public void push(int value) throws IllegalStateException
  {
    if (m_next >= m_stackData.length)
      throw new IllegalStateException("Stack full");
    m_stackData[m_next++] = value;
  }
  /**
   * Returns whether the stack is currently empty
   */
  public boolean isEmpty()
  {
    return (m_next == 0);
  }
  /**
   * Pops the top int value off the stack,
   * or throws an exception if the stack is empty
   */
  public int pop() throws IllegalStateException
  {
    if (isEmpty())
      throw new IllegalStateException("Stack empty");
    return m_stackData[--m_next]; // top item on stack
  }
  /**
   * Returns the current size of the stack
   */
  public int getStackSize()
  {
    return m_next;
  }
  /**
   * Returns the maximum size of the stack
   */
  public int getMaxStackSize()
  {
    return m_stackData.length;
  }
  /**
   * Return the value of the specified item in the stack
   * or throws an exception if the index specified is invalid.
   */
  public int get(int index) throws IllegalArgumentException
  {
    if (index < 0 || index >= getStackSize())
      throw new IllegalArgumentException("index = " + index);
    return m_stackData[index];
  }

  private int[] m_stackData;
  private int   m_next = 0; // Index of last item in stack
}

Enumerating a Stack

Now, let’s implement a class whose job it will be to enumerate all the values in an instance of Stack.  We’ll call it StackEnumerator, and as you might expect, it implements the Enumeration interface (I chose Enumeration rather than Iterator because it’s simpler):

package examples;

import java.util.Enumeration;

/**
 * Class to iterate through an instance of the above Stack class
 */
class StackEnumerator implements Enumeration
{
  /**
   * Constructor associates the StackIterator with a Stack instance
   */
  public StackEnumerator(Stack theStack)
  {
    m_theStack = theStack;
  }
  /**
   * Returns whether there are more elements
   */
  public boolean hasMoreElements()
  {
    return m_current < m_theStack.getStackSize() - 1;
  }
  /**
   * Returns the next element
   */
  public Object nextElement()
  {
    return m_theStack.get(++m_current);
  }

  private Stack m_theStack;
  private int   m_current = -1; // Position "before" first stack element
}

Note that, in order to implement this class, the Stack class needed to give it access to its internal structures — it calls the get() method of the Stack class.  However, a get() method is not really a natural feature of a stack.  In other words, we added an artifact to the Stack’s public interface (API), merely to accommodate the StackEnumerator class.  

This is not good practice!  But, as you’ll see, we can use nested and inner classes to improve the situation.

Running the Program

Now let’s see what our Stack and StackEnumerator classes can do. (Note that this is by no means a thorough test!)

package examples;

/**
 * Tests the Stack and StackIterator classes
 */
public class StackTest
{
  /**
   * Main entry point
   */
  public static void main(String[] args)
  {
    Stack aStack = new Stack(10);
    for (int i = 0; i < aStack.getMaxStackSize(); i++)
      aStack.push(i*i);
    
    StackEnumerator enumerator = new StackEnumerator(aStack);
    while (enumerator.hasMoreElements())
    {
      int value = (Integer) enumerator.nextElement();
      System.out.print(" " + value);
    }
    System.out.println();
  }
}

As you can see, the main method creates an instance of Stack, pushes some values onto the stack, and then uses a StackEnumerator to enumerate all the values on the stack. 

When run, this program outputs the following:

 0 1 4 9 16 25 36 49 64 81

But continue on to find out how inner classes can improve the situation…

Inner Class Types

There are four types of inner classes:

  • Static Nested Member Classes and Interfaces
  • Member Classes
  • Local Classes
  • Anonymous Classes

For an inner lass example, see:

https://docs.oracle.com/javase/tutorial/java/javaOO/innerclasses.html

Static Member Classes

First, we have static member classes (and static member interfaces). Such a class (or interface) is a static member of an enclosing top-level class or interface. 

Note that nested interfaces are always implicitly static members of their enclosing class.

A static member class behaves much like an ordinary top-level class, except:

  • It is enclosed within the namespace of the enclosing class, so references to it must be qualified
  • It can access the static members of the enclosing class

Let’s look at some examples…

Example 1

Now, I’ve pulled the StackEnumerator class into the Stack class as a static nested class.

I made no changes to the contents of the StackEnumerator class in the process, although I changed its name to simply Enumerator — since it’s inside the Stack class, it should be clear that it’s a stack Enumerator.

Here’s what the new Stack class looks like:

package staticNesting;

import java.util.Enumeration;

/**
 * Stack implements a simple stack of integers
 */
public class Stack
{
  /**
   * Creates a stack of specified number of elements
   */
  public Stack(int size)
  {
    m_stackData = new int[size];
  }
  /**
   * Pushes an integer value on the stack,
   * or throws an exception, if insufficient space
   */
  public void push(int value) throws IllegalStateException
  {
    if (m_next >= m_stackData.length)
      throw new IllegalStateException("Stack full");
    m_stackData[m_next++] = value;
  }
  /**
   * Returns whether the stack is currently empty
   */
  public boolean isEmpty()
  {
    return (m_next == 0);
  }
  /**
   * Pops the top int value off the stack,
   * or throws an exception if the stack is empty
   */
  public int pop() throws IllegalStateException
  {
    if (isEmpty())
      throw new IllegalStateException("Stack empty");
    return m_stackData[--m_next]; // top item on stack
  }
  /**
   * Returns the current size of the stack
   */
  public int getStackSize()
  {
    return m_next;
  }
  /**
   * Returns the maximum size of the stack
   */
  public int getMaxStackSize()
  {
    return m_stackData.length;
  }
  /**
   * Return the value of the specified item in the stack
   * or throws an exception if the index specified is invalid.
   */
  public int get(int index) throws IllegalArgumentException
  {
    if (index < 0 || index >= getStackSize())
      throw new IllegalArgumentException("index = " + index);
    return m_stackData[index];
  }

  private int[] m_stackData;
  private int   m_next = 0; // Index of last item in stack

  /**
   * A static nested class to iterate through an instance of this Stack
   */
  public static class Enumerator implements Enumeration
  {
    /**
     * Constructor associates the StackIterator with a Stack instance
     */
    public Enumerator(Stack theStack)
    {
      m_theStack = theStack;
    }
    /**
     * Returns whether there are more elements
     */
    public boolean hasMoreElements()
    {
      return m_current < m_theStack.getStackSize() - 1;
    }
    /**
     * Returns the next element
     */
    public Object nextElement()
    {
      return m_theStack.get(++m_current);
    }
    private Stack m_theStack;
    private int   m_current = -1; // Position "before" first stack element
  }
}

Here’s how we can use the modified Stack and nested Enumerator classes:

package staticNesting;

/**
 * Tests the Stack and StackIterator classes
 */
public class StackTest
{
  /**
   * Main entry point
   */
  public static void main(String[] args)
  {
    Stack aStack = new Stack(10);
    for (int i = 0; i < aStack.getMaxStackSize(); i++)
      aStack.push(i*i);

    Stack.Enumerator enumerator = new Stack.Enumerator(aStack);
    while (enumerator.hasMoreElements())
    {
      int value = (Integer) enumerator.nextElement();
      System.out.print(" " + value);
    }
    System.out.println();
  }
}

All I had to do was change the two StackEnumerator references to Stack.Enumerator

When run, this program outputs the same values as before:

 0 1 4 9 16 25 36 49 64 81

But, we can improve things even more…

An Improvement

But we can do better than that!

There are some deficiencies with this particular implementation:

  • Stack.Enumerator still knows more about the internals of Stack than it should
  • Users need to know about the nested class in order to use it;  this is really unnecessary detail for them to have to know.

So, let’s improve the situation…

Example 2

To achieve these improvements, I’ve done the following:

  • Changed the nested Enumeration class to be a private static member of Stack.
  • Changed the Stack‘s get() method to be private.
  • Added a new method to the Stack class:
    public Enumeration getEnumerator()
    By doing this, I can hide the existence of the Enumerator class.  All the user needs to know is that there is an enumerator, and that it implements the Enumeration interface.

Here’s what the new Stack class looks like:

package betterStaticNesting;

import java.util.Enumeration;

/**
 * Stack implements a simple stack of integers
 */
public class Stack
{
  /**
   * Creates a stack of specified number of elements
   */
  public Stack(int size)
  {
    m_stackData = new int[size];
  }
  /**
   * Pushes an integer value on the stack,
   * or throws an exception, if insufficient space
   */
  public void push(int value) throws IllegalStateException
  {
    if (m_next >= m_stackData.length)
      throw new IllegalStateException("Stack full");
    m_stackData[m_next++] = value;
  }
  /**
   * Returns whether the stack is currently empty
   */
  public boolean isEmpty()
  {
    return (m_next == 0);
  }
  /**
   * Pops the top int value off the stack,
   * or throws an exception if the stack is empty
   */
  public int pop() throws IllegalStateException
  {
    if (isEmpty())
      throw new IllegalStateException("Stack empty");
    return m_stackData[--m_next]; // top item on stack
  }
  /**
   * Returns the current size of the stack
   */
  public int getStackSize()
  {
    return m_next;
  }
  /**
   * Returns the maximum size of the stack
   */
  public int getMaxStackSize()
  {
    return m_stackData.length;
  }
  /**
   * Return the value of the specified item in the stack
   * or throws an exception if the index specified is invalid.
   */
  private int get(int index) throws IllegalArgumentException
  {
    if (index < 0 || index >= getStackSize())
      throw new IllegalArgumentException("index = " + index);
    return m_stackData[index];
  }

  /**
   * Returns an instance of the Enumerator class to the caller
   * as an Enumeration.  This is known as an "opaque" class.
   * All the caller needs to know is that the class being returned
   * implements the Enumeration interface.
   */
  public Enumeration getEnumerator()
  {
    return new Enumerator(this);
  }

  private int[] m_stackData;
  private int   m_next = 0; // Index of last item in stack

  /**
   * A static nested lass to enumerate an instance of this Stack
   */
  private static class Enumerator implements Enumeration
  {
    /**
     * Constructor associates the Enumerator with a Stack instance
     */
    public Enumerator(Stack theStack)
    {
      m_theStack = theStack;
    }
    /**
     * Returns whether there are more elements
     */
    public boolean hasMoreElements()
    {
      return m_current < m_theStack.getStackSize() - 1;
    }
    /**
     * Returns the next element
     */
    public Object nextElement()
    {
      return m_theStack.get(++m_current);
    }
    private Stack m_theStack;
    private int   m_current = -1; // Position "before" first stack element
  }
}

Here’s how we can use this new Stack class:

package betterStaticNesting;

import java.util.Enumeration;

/**
 * Tests the Stack and StackIterator classes
 */
public class StackTest
{
  /**
   * Main entry point
   */
  public static void main(String[] args)
  {
    Stack aStack = new Stack(10);
    for (int i = 0; i < aStack.getMaxStackSize(); i++)
      aStack.push(i*i);
    
    Enumeration enumerator = aStack.getEnumerator();
    while (enumerator.hasMoreElements())
    {
      int value = (Integer) enumerator.nextElement();
      System.out.print(" " + value);
    }
    System.out.println();
  }
}

Now, the caller knows nothing about Stack.Enumerator;  s/he only needs to know about Enumerator

When run, this program outputs the same values as before:

 0 1 4 9 16 25 36 49 64 81