Java Inner Classes
If you liked this article, go to www.descriptor.com and learn about our world-class training!
Description
Inner classes are a syntatic feature that was added to Java in JDK 1.1. They provide a convenient way for a developer to define classes within the body of another class. This article discusses the how and why of inner classes.Java Inner Classes
What Are Inner Classes?
Inner classes are classes whose definition is within the block of another class, sometimes referred to as the enclosing class. Here's a simple example:
public class EnclosingClass { public class MyInnerClass { . . . } . . . }
When you compile a class with inner classes, the compiler generates separate .class files for the enclosing and inner classes. The general naming convention for the inner-class file names is EnclosingClass$MyInnerClass.class.
Categories of Inner Classes
Java provides four different types of inner classes, as shown in Figure 1:
Figure 1: Inner Class Categories
As Figure 1 shows, we can separate inner classes into two basic categories: Member Inner Classes and Local Inner Classes. You define member inner classes at class scope, directly nested within the enclosing class's block. In other words, member inner classes "live" at the same level in the enclosing class as fields and methods. The example shown above is an example of a member inner class.
In contrast, you define local inner classes completely within a single method. They are scoped to, and can only be used by, the method in which they are defined.
Within the member category, there are two subtypes: Static inner classes and Non-Static inner classes. The distinction is that we can create instances of static inner classes without first creating an instance of the enclosing class.
Within the local category, there are also two subtypes: Named inner classes and Anonymous inner classes. As you might guess, the classes of the anonymous type have no developer-accessible name. We will cover the uses of all of these categories later in this article.
Note that while the category names shown here are widely used and descriptive, they are not in the official Java documentation.
Why Use Inner Classes?
At this point, you might be wondering what the heck good are inner classes? While you are never required to use them, they are useful, especially in a few situations:
- A class is only going to be used by a single class, and you want to scope that class's definition to the class that will use it. For example, suppose you are writing a thread class that "belongs" to some other class. It makes sense to define the thread's class within the "owner", so you don't have to create a separate source file. That helps avoid "polluting" the name space of your program with a class that's used by a single class. And by making the inner class private, you restrict its access to the enclosing class.
- Even more tightly scoped than above, if you have a class whose object(s) are only going to be used by a single method, it makes sense to use a local inner class to define and use the class totally within that method.
- You are writing a user-interface program using the Swing API. In Swing, programs often need to respond to events such as button-clicks. The anonymous inner class provides a convenient syntax for writing classes that handle events.
Static Member Inner Classes
The first category we will examine is the static member inner class, which are perhaps the most common form. Defining a static member inner class isn't really much different than definining a "normal" class, especially if you make it public - the main advantages are that you can explicitly show that the inner class "belongs" to its enclosing class and that you don't need a separate source file. Please examine the code below:
public class MyEnclosingClass { static public class MyStaticMemberInnerClass { private int y = 14; public void myMethod() { System.out.println(y); } } . . . }
Listing 2 shows a static member inner class named MyStaticMemberInnerClass. Note that its defined at the same nesting level in the enclosing class as methods and fields. Note also that it can define its own fields and methods, in this case a field named y and a method named myMethod.
Static Member Class Access to the Enclosing Class
Since the inner class is a member of its enclosing class, it makes sense that methods in the inner class should be able to access fields and call methods in the enclosing class. And it can - even if the enclosing members are private. However, there is a restriction for static inner classes: they CANNOT access any non-static member in the enclosing class. See Listing 3:
public class MyEnclosingClass { private int x = 12; static private int y = 20; static private int z = 21; static public class MyStaticMemberInnerClass { private int y = 14; public void myMethod() { // System.out.println(x); System.out.println(y); // prints 14 System.out.println(z); // prints 21 System.out.println(MyEnclosingClass.y); // prints 20 } } . . . }
In Listing 3, note that the enclosing class defines three fields, two of which are static. In the inner class, myMethod cannot access the enclosing x, since it's not static, but CAN access the other two fields, even though they are private.
Note however, just to prove a point (I would avoid this in a real program), the inner class also defines a field named y, so that name is multiply defined for methods in the inner class. So when myMethod prints y, it gets the locally defined version. To get at the y from the enclosing class, myMethod can make a static reference, specifying the enclosing class's name as shown in the last line of myMethod.
Note that access is allowed even if the enclosing members are private, which makes sense, because the inner class itself is a member of the enclosing class.
The prohibition on accessing non-static members is not just for fields, either. A static member inner class cannot directly call non-static methods in the enclosing class.
Note that an inner-class method CAN access non-static members of the enclosing class if it explicitly uses a reference to an enclosing class object:
public void myMethod() // inner-class method { . . . MyEnclosingClass mec = new MyEnclosingClass(); System.out.println(mec.x); // x is non-static in enclosing class }
Instantiating Static Member Inner Class Objects
When it comes to creating objects, static member inner classes are very similar to normal (non-inner) classes - you use the new operator in the usual way. There are two cases to consider: first, if the code that's calling new is itself contained within the enclosing class. If that's the case, you simply create objects in the normal fashion:
public class MyEnclosingClass { static public class MyStaticMemberInnerClass { . . .} private MyStaticMemberInnerClass myObj = new MyStaticMemberInnerClass(); }
But if the code using new is outside of the enclosing class, you can make a static reference to the inner class:
MyEnclosingClass.InnerClass3 myObj = new MyEnclosingClass.InnerClass3();
This syntax is consistent with accessing any static member of a class, e.g. a static method. The key point is that you don't need to first create an enclosing-class object before instantiating an inner-class object.
In summary, static member inner classes provide a straightforward way to define a class within the boundary of an enclosing class. They let you clearly show that the inner class is part of, or belongs to the enclosing class, and let you use familiar syntax to create objects.
Non-Static Member Inner Classes
Like static member inner classes, you define these non-static member inner classes within the boundary of enclosing class. The obvious first difference is that these don't use the static keyword, as shown in Listing 4:
public class MyEnclosingClass { . . . public class MyNonStaticMemberInnerClass { private int x = 14; public void myMethod() { System.out.println(x); } } . . . }
There are a couple of ramifications of omitting static:
- These type of inner classes have unfettered access to all of the enclosing class's members, be those members static or not.
- Instantiating inner-class objects is a bit trickier, since you must first instantiate an enclosing-class object.
Non-Static Member Class Access to the Enclosing Class
Unlike static member inner classes, non-static inner classes have no restrictions on what they can access from the enclosing class. See Listing 5:
public class MyEnclosingClass { private int x = 12; static private int z = 21; public class MyNonStaticMemberInnerClass { private int x = 14; private static int y = 17; public void myMethod() { System.out.println(y); System.out.println(z); System.out.println(x); // same as System.out.println(this.x); System.out.println(MyEnclosingClass.this.x); } } . . . }
In Listing 5, note how myMethod (within the inner class) can access ANY member, field or method, from the enclosing class, be it static or not.
Also note the odd syntax required if an inner class defines a member with the same name as a member from the enclosing class, in this case, the field named x. The implication is that within an non-static inner class, there are actually two this references - the this for the inner class itself and the this for the enclosing class.
Instantiating Non-Static Member Inner Class Objects
With non-static member inner classes, there's a twist to creating objects - Java requires that an enclosing-class object must first exist. There are a couple of cases to consider.
First, if you are writing code in a non-static enclosing class method, we are assured that an enclosing-class object exists - otherwise the method would not be running! In other words, within non-static enclosing-class methods, there's a valid this reference. In that case, creating instances of the non-static member inner class is trivial:
public class MyEnclosingClass { public class MyNonStaticMemberInnerClass { . . .} public void myEnclosingMethod() { MyNonStaticMemberInnerClass obj = new MyNonStaticMemberInnerClass(); . . . } }
But if the code that wants an inner-class object is outside of the enclosing class, then there's a different syntax for the new operator:
MyEnclosingClass mec = new MyEnclosingClass(); MyEnclosingClass.MyNonStaticMemberInnerClass obj = mec.new MyNonStaticMemberInnerClass();
Note that for this to work, you must first instantiate an enclosing-class object, and then use the odd syntax on the new operator to instantiate the inner-class object.
In summary, non-static member inner classes have full access to enclosing-class members, at the expense of a more complex way to create objects.
Introduction to Local Inner Classes
The inner classes we've covered so far have been member inner classes, which means that they were members of the enclosing class in the same way that methods and fields are members. Now we will cover local inner classes, which are even more tightly scoped - local inner classes reside within, and are used by only by a single method. These local inner classes are useful if you have methods that need "one-off" classes that you won't use anywhere else.
Local inner classes come in two flavors:
- Named Local Inner Classes that have a name (obviously) and thus you can create multiple instances.
- Anonymous Local Inner Classes that have no application-assigned name. The catch is with these is that you only can have a single instance of these classes. In fact, you define anonymous classes and create the object all in one fell swoop!
Named Local Inner Classes
Named Local Inner Classes are useful if you have a class that will be used within a single method and that method needs more than one object of that class. The syntax is relatively straightforward: you simply define a class within the block of the method as shown in Listing 6:
public class MyEnclosingClass { public void anEnclosingMethod() { class MyNamedLocalInnerClass { private int x; public void myMethod() { System.out.println("x: " + x); } } } }
In Listing 6, the MyNamedLocalInnerClass is physically defined within the block of the anEclosingMethod and thus is scoped to that method. The inner class, as you'd expect, can define its own fields and methods. Note that in this case, the inner class is defined at the beginning of the enclosing method, but that's not required. Also note that when you define these classes, you cannot use the public, private or static keywords (and other class options) since those keywords are only legal for class members.
Named Local Inner Class Access to the Enclosing Class
As you might expect, methods in the inner class can access fields and methods defined in the enclosing class. There is a restriction however: if the enclosing method is static, then the inner class can only access static members of the enclosing class.
And there's another twist: since these classes are defined within a method, they can access local variables that are defined by the enclosing method, as long as:
- The local variable is defined lexically ABOVE the inner class.
- The local variable is declared as final.
See Listing 7 for an example:
public class MyEnclosingClass { private int y; public void anEnclosingMethod() { final int z; int w; class MyNamedLocalInnerClass { private int x; public void myMethod() { System.out.println(y); System.out.println(z); System.out.println(x); } } } }
Since MyNamedLocalInnerClass is defined within a non-static method, it can access any field or method in the enclosing class, be they static or not. So it's OK for it to print the y field's value.
And it's OK to access the local variable z, since it's declared as final, but you will get a compile error if you try to access w, since it's not final.
Instantiating Named Local Inner Class Objects
Instantiating objects from named local inner classes is straightforward, as long as you remember that the local inner class is scoped to the enclosing method:
public void anEnclosingMethod() { class MyNamedLocalInnerClass { . . . public void myMethod() {} . . . } MyNamedLocalInnerClass mnlic = new MyNamedLocalInnerClass(); mnlic.mymethod(); }
Note that there's nothing stopping you from creating multiple instances of a named local inner class - in fact, that's the major distinguishing characteristic of these local classes as compared to anonymous local inner classes.
Anonymous Local Inner Classes
The final category of inner classes is anonymous local inner classes, which as you might expect, don't have a name assigned by the developer. They actually DO have a name assigned by the JVM, but the program doesn't refer to it.
These type of inner classes have the oddest syntax of all: you define the class and instantiate an object in one fell swoop. In fact, the syntax to do this is actually a Java expression, not a complete statement - that means you can create anonymous classes and an object within a larger statement, say a call to a method such as System.out.println.
But remember, these are still local inner classes, which means that they are scoped to the method that uses them.
When you define an anonymous class, you must specify either a class or an interface. If you specify a class, then Java uses that class as the anonymous class's superclass. If you specify an interface, then Java uses Object as the superclass and the anonymous class implements the interface and thus must provide any methods in the interface.
An Anonymous Inner Class with an Explicit Superclass
Now let's look at the syntax of an anonymous inner class where we explicitly specify a superclass constructor. See Listing 8.
public class MyEnclosingClass { public void myEnclosingMethod() { System.out.println( new Object() { } ); } }
In Listing 8, we defined an anonymous class and created an object. In this case, the we chose java.lang.Object as the superclass, but it can be any class you wish. We also decided to embed the expression within a call to System.out.println, just to show how that works. Please be sure that you can see the open and close parenthesis and the semicolon for the System.out.println call - the stuff inside is the expression to define an anonymous class and create an object.
In Listing 8, the newly created class has no additional fields or methods, so it's not very useful. Listing 9 shows a slightly more interesting anonymous class:
public class MyEnclosingClass { public void myEnclosingMethod() { System.out.println( new Object() { private int x = 17; @Override public String toString() { return "Inner class x: " + x; } } ); } }
In Listing 9, the anonymous class defines a field named x as well as overriding the toString method from Object. This is still not a very useful class, but it does illustrate the concepts.
And note: we defined the inner class within a call to System.out.println. Recall that System.out.println automatically calls toString if you pass it an object. So whenever we call myEnclosingMethod, it will print the string "Inner class x: 17". Don't forget that when you use an anonymous class, Java creates not only defines a new class, but creates an object too!
An Anonymous Inner Class that Specifies an Interface
Instead of specifying a superclass constructor, you can specify an interface when you define an anonymous class. In that case, Java subclasses Object and causes the anonymous class to implement the interface. Therefore, the anonymous class MUST define all of the methods in the interface, or else the code will not compile. See Listing 10 for an example.
public class MyEnclosingClass { public void myEnclosingMethod() { System.out.println( new java.io.Serializable() { private int x = 18; @Override public String toString() { return "Inner class x: " + x; } } ); } }
In Listing 10, within a call to System.out.println, we define an anonymous class that extends Object and implements the Serializable interface. In this case, the inner class doesn't need to have any methods, since Serializable doesn't define any. However, to make this example work like the last one, we overrode toString.
Anonymous Class Access to the Enclosing Class
Since anonymous classes are local inner classes, they have the same access to the enclosing class and method as the named category. That is, they can access fields and methods from the enclosing class and local variables in the enclosing method, as long as the local variables are defined as final.
Anonymous Classes as Event Handlers
So far, the anonymous classes we've shown have illustrated syntax, but haven't been very useful in the real world. Now let's see how to use an anonymous class as an event handler callback for a JavaBean event.
JavaBeans are classes that follow the JavaBean specification, such as providing get/set methods. But in addition to providing properties, JavaBeans can also "fire" events, which let the bean notify other objects that something significant happened to the bean. Events are most often used in GUI applications, for example, a Java GUI could have a pushbutton JavaBean that fires an event when the user presses the button. The event handler can then respond to the button-press event.
To be a JavaBean event handler, a class must implement one or more interfaces as defined by the JavaBean. In the case of a JButton, one such event interface is java.awt.event.ActionListener, which looks like:
public interface ActionListener { public void actionPerformed(ActionEvent evt); }
A JButton fires this event when the user presses the button. By "firing", we mean that it calls the actionPerformed method. So any event-handler class for this event must implement ActionListener and provide an implementation for the actionPerformed method. Listing 11 shows using an anonymous inner class for this purpose:
package anonymous; import java.awt.BorderLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.JButton; import javax.swing.JFrame; public class MyGUI { private JFrame myFrame; private JButton myButton; public MyGUI() { myFrame = new JFrame("My Frame"); myFrame.getContentPane().setLayout(new BorderLayout()); myButton = new JButton("My Button"); myFrame.getContentPane().add(myButton, "Center"); myButton.addActionListener( new ActionListener() { public void actionPerformed(ActionEvent evt) { System.out.println("Button was pressed!!"); } } ); myFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); myFrame.setSize(500, 500); myFrame.setVisible(true); } public static void main(String[] args) { MyGUI gui = new MyGUI(); } }
In Listing 11, look closely at the MyGUI class constructor and note how we use an anonymous inner class to register an event handler for the button-push event. In this case, we simply write a string to the console, but this technique is widely used in Java GUIs that actually do interesting work. A common approach is to have the event-handler method call a private method that does the actual work of responding to the event.
Summary
Java provides four categories of inner classes, each of which that have a different syntax and typical usage.