Try an online Java class for free!
Additional Resources

Inner Classes

In this lesson of the Java tutorial, you will learn...
  1. To gain an understanding of the creation and use of inner 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 containing class (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
    • 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.java, 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

Inner Class Syntax

Syntax
[modifiers] class OuterClassName {
 code
 [modifiers] class InnerClassName {
  code
 }
}

The definition of the inner class is always available for the outer class to use

  • 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);
  }
 }
}
Code Explanation

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
  • and 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:

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);
 }
}
Code Explanation

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 instantiate and reference 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 you instantiate 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:

Syntax
 OuterClassName.InnerClassName innerClassVariable

To create a non-static inner class object from outside the enclosing class, you must attach it to an outer class instance

Syntax
 outerClassVariable.new InnerClassName(arguments)

For this purpose the new operator is a binary operator - it creates a new object of the type on its right belonging to the object on its left.

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

Syntax
 OuterClassName.new InnerClassName(arguments)

Code Sample: Java-InnerClasses/Demos/Inner2.java

public class Inner2 {
 public static void main(String[] args) {
  MyOuter mo;
  MyOuter.MyInner momi;
  mo = new MyOuter(1, 2);
  momi = mo.new MyInner(102);
  momi.publicDisplay();
 }
}
Code Explanation

In the main method, the variable momi for the inner class instance is typed as MyInner.MyOuter

It is populated by instantiating it attached to mo, using mo.new MyInner(102)

It is generally recommended to avoid this syntax, and rely instead on outer class code to instantiate the inner class objects

  • making the inner constructors private or protected will enforce this practice (generally protected is better, since that would allow extensions of the outer class to also create inner class objects)

Note that we can access any public elements of the inner class instance, as in momi.publicDisplay();

You can create a new, unnamed, outer class object to hold the inner class object at the same time as creating the inner class object

Syntax
new OuterClassName(arguments).new InnerClassName(arguments)

For example:

momi = (new MyOuter(1)).new MyInner(101);

Working with Inner Classes

The previous section's code is rather complex

  • it is easer if the 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);
 }
}
Code Explanation

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

  • 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

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

  • 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

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

Code Sample: Java-InnerClasses/Demos/PayrollInnerClass/employees/Employee.java

package employees;
import finance.TransactionException;

public class Employee {
---- Code Omitted ----
private double ytdPay; private Payment[] payments = new Payment[12]; private int paymentCount = 0;
---- Code Omitted ----
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; } } }
Code Explanation

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();
 }
 
}
Code Explanation

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 {
---- Code Omitted ----
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; } } }
Code Explanation

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();
 }
 
}
Code Explanation

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

Inner Classes Conclusion

In this lesson of the Java tutorial you have learned:

  • How to declare and use inner classes
To continue to learn Java go to the top of this page and click on the next lesson in this Java Tutorial's Table of Contents.
Last updated on 2009-03-02

Use of http://www.learn-java-tutorial.com (Website) implies agreement to the following:

Copyright Information

All pages and graphics on Website are the property of Webucator, Inc. unless otherwise specified.

None of the content on Website may be redistributed or reproduced in any way, shape, or form without written permission from Webucator, Inc.

No Printing or saving of pages or content on Website

This content may not be printed or saved. It is for online use only.


Linking to Website

You may link to any of the pages on Website; however, you may not include the content in a frame or iframe without written permission from Webucator, Inc.


Warranties

Website is provided without warranty of any kind. There are no guarantees that use of the site will not be subject to interruptions. All direct or indirect risk related to use of the site is borne entirely by the user. All code and explanations provided on this site are provided without warranties to correctness, performance, fitness, merchantability, and/or any other warranty (whether expressed or implied).


For individual private use only

You agree not to use this online manual to deliver or receive training. If you are delivering or attending a class that is making use of this online manual, you are in violation of our terms of service. Please report any abuse to courseware@webucator.com. If you would like to deliver or receive training using this manual, please fill out the form at http://www.webucator.com/Contact.cfm