MENU
The instructor was extremely organized and knowledgeable about Microsoft Project, and her class has enabled me...More Testimonials »

Inner Classes

In this lesson, you will learn how inner classes are used in Java.

Lesson Goals

  • Learn about the creation and use of inner classes.
  • Learn how to create and use enum classes.

Inner Classes, aka Nested Classes

Inner classes, also known as nested classes are classes defined within another class.

They may be defined as public, protected, private, or with package access.

They may only be used "in the context" of the containingclass (outer class, or enclosing class), unless they are marked as static.

  • The outer class can freely instantiate inner class objects within its code; they are automatically associated with the outer class instance that created them.
  • Code in some other class can instantiate an inner class object associated with a specific instance of the outer class if the inner class definition is public (and its containing class is public as well).
  • If the inner class is static, then it can be instantiated without an outer class instance, otherwise, the inner class object must be attached to an instance of the outer class.

Inner classes are used to (these uses overlap to some extent):

  • Create a type of object that is only needed within one class, usually for some short-term purpose.
  • Create a utility type of object that cannot be used elsewhere (which would allow the programmer to change it without fear of repercussions in other classes).
  • Create one-of-a-kind interface implementations (such as individualized event handlers).
  • Allow a sort of multiple inheritance, since the inner class may extend a different class than the outer class extends, and an inner class instance would have access to its private elements as well as the private elements of the outer class object it is attached to
  • Implement one-to-many relationships where the classes are tightly coupled (meaning that code for one or both of the classes needs access to many of the private elements of the other class) - the outer class would be the "one" side of the relationship, with the inner class being the "many" side.
  • Provide a specialized form of callback, with which a class may pass very limited access to some of its internal components. Note that:
    • The collections classes provide an iterator, a class that implements the Iterator interface to loop through the elements in the collection using hasNext and next methods.
    • Given that the internal structure of the collection may be complex, implementing the iterator as an inner class enables it to navigate the structure, while not exposing any other aspects of the collection to the outside world.

Inner class code has free access to all elements of the outer class object that contains it, by name (no matter what the access level of the elements is).

Outer class code has free access to all elements in any of its inner classes, no matter what their access term.

An inner class compiles to its own class file, separate from that of the outer class (the name of the file will be OuterClassName$InnerClassName.class, although within your code the name of the class will be OuterClassName.InnerClassName); you cannot use the dollar sign version of the name in your code.

An inner class occupies its own memory block, separate from the outer class memory block.

An inner class may extend one class, which might be unrelated to the class the outer class extends.

An inner class can implement one of more interfaces, and, if treated as an instance of one of its interfaces, external code may have no knowledge that the object actually comes from an inner class.

Inner Class Syntax

[modifiers] class OuterClassName {
	code
	[modifiers] class InnerClassName [extends BaseClassToInner] [SomeInterface[, MoreInterfaces, ...]] {
		fields and methods
	}

The definition of the inner class is always available for the outer class to use. Note that:

  • No inner class objects are automatically instantiated with an outer class object.
  • Outer class code may instantiate any number of inner class objects - none, one, or many.

Code Sample:

Java-InnerClasses/Demos/MyOuter.java
public class MyOuter {
  private int x;
  MyOuter(int x, int y) {
    this.x = x;
    new MyInner(y).privateDisplay();
  }
  public class MyInner {
    private int y;
    MyInner(int y) {
      this.y = y;
    }
    private void privateDisplay() {
      System.out.println("privateDisplay x = " + x + " and y = " + y);
    }
    public void publicDisplay() {
      System.out.println("publicDisplay x =  " + x + " and y = " + y);
    }
  }
}

This is a simple example of an inner class

  • MyOuter has one property, x; the inner class MyInner has one property, y.
  • The MyOuter constructor accepts two parameters; the first is used to populate x.
  • It creates one MyInner object, whose y property is populated with the second parameter.
  • Note that the inner class has free access to the private outer class x element.
  • The outer class has free access to the private inner class privateDisplay() method.

The connection between the two classes is handled automatically.

The following diagram maps out the memory used by the example.

Outer Class/Inner Class Memory Map

Instantiating an Inner Class Instance from within the Enclosing Class

An inner class instance may be directly instantiated from code in the enclosing class, without any special syntax:

[modifiers] class OuterClassName {
	code
	[modifiers] class InnerClassName {
		code
	}
	public void someMethod() {
		InnerClassName variable = new InnerClassName();
	}
}

Such an instance is automatically associated with the enclosing class instance that instantiated it.

Code Sample:

Java-InnerClasses/Demos/Inner1.java
public class Inner1 {
  public static void main(String[] args) {
    new MyOuter(1, 2);
  }
}

This code simply creates an instance of the outer class, MyOuter.

The MyOuter constructor creates an instance of MyInner as mentioned earlier.

Inner Classes Referenced from Outside the Enclosing Class

If the access term for the inner class definition is public (or the element is accessible at package access or protected level to the other class), then other classes can hold references to one or more of these inner class objects

  • If the inner class is static, then it can exist without an outer class object, otherwise any inner class object must belong to an outer class instance.

For code that is not in the outer class, a reference to a static or non-static inner class object must use the outer class name, a dot, then the inner class name:

OuterClassName.InnerClassName innerClassVariable

If the inner class has an accessible constructor, you can you instantiate one from outside of the enclosing class, although the syntax is ugly, and there is rarely a need for this capability.

Referencing the Outer Class Instance from the Inner Class Code

If inner class code needs a reference to the outer class instance that it is attached to, use the name of the outer class, a dot, and this. Remember that if there is no name conflict, there is no need for any special syntax.

For code in MyInner to obtain a reference to its MyOuter:

MyOuter.this

static Inner Classes

An inner class may be marked as static.

A static inner class my be instantiated without an instance of the outer class. Note that:

  • static members of the outer class are visible to the inner class, no matter what their access level.
  • Non-static members of the outer class are not available, since there is not instance of the outer class to retrieve them from.

To create a static inner class object from outside the enclosing class, you must still reference the outer class name

new OuterClassName.InnerClassName(arguments)

An inner class may not have static members unless the inner class is itself marked as static.

Code Sample:

Java-InnerClasses/Demos/StaticInnerTest.java
class StaticOuter {
  public StaticInner createInner() {
   return new StaticInner();
  }

  static class StaticInner {
    public void display() {
      System.out.println("StaticOuter.Inner display method");
    } 
  }
}

class StaticInnerTest  {
  public static void main(String[] args) {
    
    new StaticOuter.StaticInner().display();
    
    StaticOuter so = new StaticOuter();
    
    //so.new StaticInner().display();
    
    so.createInner().display();

 
  }
}

We have a class StaticOuter that declares a static inner class StaticInner. StaticOuter has a method that will create instances of StaticInner. But, StaticInner also has a public constructor. Note that:

  • We can directly instantiate a StaticOuter.StaticInner object without an outer class instance.
  • Code for a StaticOuter can create a StaticInner, but the inner class object has no attachment to the outer class object that created it.
  • Note the commented out line; you cannot create a static inner class instance attached to an instance of its enclosing class.

Better Practices for Working with Inner Classes

It is easiest if inner class objects can always be instantiated from the enclosing class object. You can create a factory method to accomplish this.

Code Sample:

Java-InnerClasses/Demos/FactoryInnerOuter.java
class FactoryOuter {
  FactoryInner[] fi = new FactoryInner[3];
  protected int lastIndex = 0;
  private int x = 0;
  public FactoryOuter(int x) {
    this.x = x;
  }
  public int getX() {
    return x;
  }
  public void addInner(int y) {
    if (lastIndex < fi.length) {
      fi[lastIndex++] = new FactoryInner(y);
    } 
    else throw new RuntimeException("FactoryInner array full");
  }
  public void list() {
    for (int i = 0; i < fi.length; i++) {
      System.out.print("I can see into the inner class where y = " + 
                       fi[i].y + " or call display: ");
      fi[i].display();      
    }
  }
  public class FactoryInner {
    private int y;
    protected FactoryInner(int y) {
      this.y = y;
    }
    public void display() {
      System.out.println("FactoryInner x =  " + x + " and y = " + y);
    }
  }
}
public class FactoryInnerOuter {
  public static void main(String[] args) {
    FactoryOuter fo = new FactoryOuter(1);
    fo.addInner(101);
    fo.addInner(102);
    fo.addInner(103);
    fo.list();
    //fo.addInner(104);
  }
}

For convenience, this file contains both the main class and the FactoryOuter class (with package access). Note that:

  • An instance of FactoryOuter contains a three element array of FactoryInner objects.
  • The addInner method instantiates a FactoryInner object and adds it to the array (note that is still automatically associated with the FactoryOuter instance by the JVM, but we need our own mechanism for keeping track of the inner class instances we create).
    • A better approach would be to use one of the collections classes instead of an array, to avoid running out of room in the array

This is exactly the sort of thing that happens when you obtain an iterator from a collection class. In order to successfully navigate what is most likely a complex internal structure, the object will need access to the private elements. So, an inner class is used, but all you need to know about the object is that it implements the Iterator interface.

Code Sample:

Java-InnerClasses/Demos/PayrollInnerClass/employees/Employee.java
package employees;
import finance.TransactionException;

public class Employee {

---- C O D E   O M I T T E D ----
  private double ytdPay;
  private Payment[] payments = new Payment[12]; 
  private int paymentCount = 0;  

---- C O D E   O M I T T E D ----
  public double getYtdPay() { return ytdPay; }
  
  public Payment createPayment() {
    Payment p = new Payment(payRate);
    payments[paymentCount++] = p;
    return p;
  }

  public void printPaymentHistory() {
    for (Payment p : payments) {
      System.out.println(p);
    }
  }
  
  public class Payment {
    private double amount;
    private boolean posted;
    
    public Payment(double amount) {
      this.amount = amount;
    }

    public boolean process() throws TransactionException {
      if (!posted) {
        ytdPay += amount;
        posted = true;
        System.out.println(getFullName() + " paid " + amount);
        return true;
      } else {
        throw new TransactionException("Transaction already processed");
      }
    }
    
    public String toString() {
      return getFullName() + " payment of " + amount;
    }
    
  } 
}

Payment is an inner class to a simplified Employee, and, as an inner class, has free access to all private elements of Employee. Unlike a standalone payment class, this class can retrieve the employee name from the outer class instance. We also use this access to defer updating the year-to-date amounts until the payment is posted, via the process method.

To get this degree of interaction between two separate classes would be difficult, since it would mean that either:

  1. The ability to update ytdPay would have to be publicly available.
  2. Employee and Payment would have to be in the same package, with updating ytdPay achieved by using package access.

Note that we have also separated the concepts of creating a payment from actually posting it. This gives us better control over transactions - note that a payment cannot be processed twice.

Code Sample:

Java-InnerClasses/Demos/PayrollInnerClass/Payroll.java
import employees.*;
import finance.*;

public class Payroll {
  public static void main(String[] args) {

    Employee.setNextId(22);
    Employee e = new Employee("John", "Doe", 6000.0);
    
    // loop to pay each month
    for (int month = 0; month < 12; month++) {
      Employee.Payment p = e.createPayment();
      try {
        p.process();
        
        // HR error causes attempt to process June paycheck twice
        if (month == 5) p.process();
      }
      catch (TransactionException te) {
        System.out.println(te.getMessage());
      }
      System.out.println("Ytd pay: " + e.getYtdPay());
    }     
    
    System.out.println("Employee Payment History:");
    
    e.printPaymentHistory();
  }
  
}

We have only one employee for simplicity. As we loop for each month, a payment is created for each. We try to process the June payment twice (remember that the array is zero-based, so January is month 0; this matches the behavior of the java.util.Date class) . The second attempt to process the payment should throw an exception which our catch block handles.

We retrieve and print the year-to-date pay each time we process a payment.

At the end, we have the Employee object print the entire payment history created by our calls to the inner class' process method..

Code Sample:

Java-InnerClasses/Demos/PayrollInnerClassInterface/employees/Employee.java
package employees;
import finance.*;

public class Employee {

---- C O D E   O M I T T E D ----
  public class Payment implements Payable {
    private double amount;
    private boolean posted;
    
    public Payment(double amount) {
      this.amount = amount;
    }

    public boolean process() throws TransactionException {
      if (!posted) {
        ytdPay += amount;
        posted = true;
        System.out.println(getFullName() + " paid " + amount);
        return true;
      } else {
        throw new TransactionException("Transaction already processed");
      }
    }
    
    public String toString() {
      return getFullName() + " payment of " + amount;
    }
    
  } 
}

This code goes one step further to create a Payment inner class that implements the Payable interface.

Code Sample:

Java-InnerClasses/Demos/PayrollInnerClassInterface/Payroll.java
import employees.*;
import finance.*;

public class Payroll {
  public static void main(String[] args) {

    Employee.setNextId(22);
    Employee e = new Employee("John", "Doe", 6000.0);
    
    // loop to pay each month
    for (int month = 0; month < 12; month++) {
      Payable p = e.createPayment();
      try {
        p.process();
        
        // HR error causes attempt to process June paycheck twice
        if (month == 5) p.process();
      }
      catch (TransactionException te) {
        System.out.println(te.getMessage());
      }
      System.out.println("Ytd pay: " + e.getYtdPay());
    }     
    
    System.out.println("Employee Payment History:");
    
    e.printPaymentHistory();
  }
  
}

The only difference here is that we declare the variable holding the payments as Payable, hiding the fact that it is an inner class.

Enums

In Java 5, the enum element was introduced. Long sought by the C/C++ part of the Java community, enums provide a set of predefined constants for indicating a small set of mutually exclusive values or states.

Why Another Syntax Element for a Set of Constants?

The other approaches all have some sort of flaw, particularly as involves type-safety.

  • Interfaces defining only constants were commonly used, and several are grandfathered into the API, like SwingConstants. But, there is a minor problem that if you implement an interface in order to gain the constants, you then have additional public elements in that class that you wouldn't really want to provide to the outside world.
    public class MyFrame extends JFrame implements SwingConstants { . . . }
    
    MyFrame frame = new MyFrame();
    // frame.HORIZONTAL is now publicly available
    
    While not terrible, there isn't any real meaning to frame.HORIZONTAL, or any reason we would want to make it available to the outside world.
  • Using plain old integers seems straightforward enough, but, if you perhaps have a method that requires one of that set of values to be passed in, the parameter would be typed as int. A caller could supply any int value, including ones you wouldn't expect.
    private int LEFT = 0;
    private int RIGHT = 1;
    private int CENTER = 2;
    
    public void setAlignment(int align) { ... }
    
    // compiler would allow:
    setAlignment(99);

Java enums provide a type-safe way of creating a set of constants, since they are defined as a class, and therefore are a type of data.

A disadvantage to this approach is that the set of values is written into the code. For sets of values that may change, this would require recompiling the code, and would invalidate any serialized instances of the enum class. For example, if we offered a choice of benefits plans to our employees, the set of available plans would not be a good candidate for an enum, since it is likely that the set of available plans would eventually change.

Defining an enum Class

To create a simple enum class:

  1. Declare like an ordinary class, except using the keyword enum instead of class.
  2. Within the curly braces, supply a comma-separated list of names, ending in a semicolon.

One instance of the enum class will be created to represent each item you listed, available as a static field of the class, using the name you supplied which will be the individual values. Each instance can provide an integral value, with sequential indexes starting at 0, in the order that the names were defined - there is no way to change this, but there is a route to get specific values which have a complex internal state.

public enum EnumName {
	value1, value2, value3, . . . ;
}
public enum Alignment { left, right, center; }

There will be three instances of the class created, Alignment.left, Alignment.right, and Alignment.center. An Alignment type variable can hold any of these three values.

Enums automatically extend the Enum class from the API, and they inherit several useful methods:

  • Each object has a name method that returns the name of that instance (as does the toString method).
  • The ordinal method returns that enum object's position in the set, the integer index mentioned above.

There are also several other methods that will be present, although they are not listed in the documentation for Enum.

  • A public static EnumName[] values() method that returns an array containing all the values, in order (so that the array index would match the ordinal value for that object). This method is not inherited, but is built specifically for each enum class.
  • A public EnumName valueOf(String name) method that returns the instance whose name matches the specified name (this is not the uglier method you will see in the documentation, but another built specifically for each instance - the one listed in the documentation is actually used internally by the simpler form).

The reason for the last two methods not being in the documentation has to do with generics and type erasure - the methods cannot be declared in the Enum base class in a way that would allow the use of the as-yet unknown subclass.

Individual values from the set may be accessed as static elements of the enum class. The JVM will instantiate exactly one instance of each value from the set. Therefore, they can be used in comparisons with ==, or in switch statements (using the equals method is preferred to ==, since it will serve as a reminder that you are dealing with true objects, not integers).

Although enums may be top-level classes, they are often created as inner classes, as in the following example, where the concept of the enum is an integral part of a new BookWithEnum class. When used as an inner class, they are automatically static, so that an instance of an inner enum does not have access to instance elements of the enclosing class.

Code Sample:

Java-InnerClasses/Demos/BookWithEnum.java
public class BookWithEnum {
  private int itemCode;
  private String title;
  private double price;
  private Category category;
  
  public enum Category { required, supplemental, optional, unknown };
  
  public BookWithEnum(
        int itemCode, String title, 
        double price, Category category) {
    setItemCode(itemCode);
    setTitle(title);
    setPrice(price);
    setCategory(category);
  }
  public BookWithEnum(String title) {
    setItemCode(0);
    setTitle(title);
    setPrice(0.0);
    setCategory(Category.unknown);
  }
  public int getItemCode() {
    return itemCode;
  }
  public void setItemCode (int itemCode) {
    if (itemCode > 0) this.itemCode = itemCode; 
  }
  public String getTitle() {
    return title;
  }
  public void setTitle (String title) {
    this.title = title;
  }
  public void setPrice(double price) {
    this.price = price;
  }
  public double getPrice() {
    return price;
  }
  public void setCategory(Category category) {
    this.category = category;
  }
  public void setCategory(String categoryName) {
    this.category = Category.valueOf(categoryName);
  }
  public Category getCategory() {
    return category;
  }
  public void display() {
    System.out.println(itemCode + " " + title + ": " + category +
          ", Price: $" + price);
  }
}

The Category enum is defined as an inner class to BookWithEnum. The full names of the complete set of values are: BookWithEnum.Category.required, BookWithEnum.Category.supplemental, BookWithEnum.Category.optional, and BookWithEnum.Category.unknown. From within the BookWithEnum class, they may be accessed as: Category.required, Category.supplemental, Category.optional, and Category.unknown.

We set the category for a book constructed without one as Category.unknown, and provide methods to get the value, and to set it with either an enum object or from a string.

Note that enums may be used in switch statements - for the cases you use only the short name for the value.

More Complex Enums

Enums are more than just a set of integer constants. They are actually a set of unique object instances, and, as objects, can have multiple fields. So, an enum is a class with a fixed number of possible instances, each with it's own unique state, and each of the possible instances is created automatically and stored as static field under the same name. (In design pattern terms, an enum is a Flyweight - a class where only a limited number of fixed states exist.)

To create a more complex enum class:

  1. Declare as before.
  2. Declare any additional fields and accessor methods as with a regular class. While you can actually write mutator methods to create what is called a mutable enum, this practice is strongly discouraged.
  3. Write one constructor.
  4. Within the curly braces, again supply a comma-separated list of names, which will be the individual values, but this time with a parameter list. The enum values will be constructed with the data you provide.

← Collections

Client Success
  1. Compare Us
  2. Client List
  3. Testimonials
Join The Team
  1. Learn how you can become a Webucator Trainer
  2. Career Opportunities
Locations
© Webucator, Inc. All rights reserved. |Toll Free: 1-877-932-8228Toll Free: 1-877-932-8228 |Outside the USA: 315-849-2724|Fax: 315-849-2723