Inner Classes
- 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
[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);
}
}
}
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
![]()
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 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:
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
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
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();
}
}
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
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);
}
}
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;
}
}
}
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:
- the ability to update ytdPay would have to be publicly available
- 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 {
---- 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;
}
}
}
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.
Inner Classes Conclusion
In this lesson of the Java tutorial you have learned:
- How to declare and use inner classes