Friday, February 25, 2011

Chapter 52: Inner Classes

You’re an Java programmer, so you know that for reuse and flexibility/extensibility you need to keep your classes specialized. In other words, a class should have code only for the things an object of that particular type needs to do; any other behavior should be part of another class better suited for that job. Sometimes, though, you find yourself designing a class where you discover you need behavior that belongs in a separate, specialized class, but also needs to be intimately tied to the class you’re designing.

Inner classes are an integral part of any Java application. There are many types of Inner classes and features that they provide make them very useful for any application. In this chapter, we are going to take a look at these inner classes in detail.

So, lets get started!!!

A Normal Inner Class

We use the term normal to represent inner classes that are not
• Static
• Method-local
• Anonymous

Before you jump up and ask me what about these types of inner classes? Relax my friend, the subsequent chapters will be dedicated to them exclusively. So for this chapter, we will be looking at the normal or basic inner classes and their usage.

You define an inner class within the curly braces of the outer class:

class ExampleOuterClass {
class ExampleInnerClass { }
}

Piece of cake. And if you compile it,
%javac ExampleOuterClass.java

you’ll end up with two class files:
ExampleOuterClass.class
ExampleOuterClass$ExampleInnerClass.class

The inner class is still, in the end, a separate class, so a separate class file is generated for it. But the inner class file isn’t accessible to you in the usual way. You can’t say

%java ExampleOuterClass$ExampleInnerClass

in hopes of running the main() method of the inner class, because a regular inner class can’t have static declarations of any kind. The only way you can access the inner class is through a live instance of the outer class! In other words, only at runtime when there’s already an instance of the outer class to tie the inner class instance to. First, let’s put some more code inside these classes:

class ExampleOuterClass {
private int x = 7;

// inner class definition
class ExampleInnerClass {
public void accessOuter() { System.out.println("Outer x is " + x);
}

} // close inner class definition

} // close outer class

The preceding code is perfectly legal. Notice that the inner class is indeed accessing a private member of the outer class. That’s fine, because the inner class is also a member of the outer class. So just as any member of the outer class can access any other member of the outer class, private or not, the inner class, also a member can do the same.


Instantiating an Inner Class

To create an instance of an inner class, you must have an instance of the outer class to tie to the inner class. There are no exceptions to this rule: an inner class instance can never stand alone without a direct relationship to an instance of the outer class. NO EXCEPTIONS TO THIS RULE. You cannot instantiate an inner class without a valid outer class instance already being present.

Instantiating an Inner Class from Within the Outer Class

Most often, it is the outer class that creates instances of the inner class, since it is usually the outer class wanting to use the inner instance as a helper for its own use. We’ll modify the ExampleOuterClass class to create an instance of ExampleInnerClass:

class ExampleOuterClass {
private int x = 7;
public void instantiateInnerClass() {
ExampleInnerClass in = new ExampleInnerClass();
// make an inner instance
in.accessOuter();
}

class ExampleInnerClass {
public void accessOuter() {
System.out.println("Outer x is " + x);
}
}
}

You can see in the preceding code that the ExampleOuterClass code treats ExampleInnerClass just as though ExampleInnerClass were any other accessible class—it instantiates it using the class name (new ExampleInnerClass()), and then invokes a method on the reference variable (in.accessOuter()). But the only reason this syntax works is because the outer class instance method code is doing the instantiating. In other words, there’s already an instance of the outer class—the instance running the instantiateInnerClass() method.

You might be tempted to think that since we have already said that we cannot instantiate an inner class without an outer class instance, there is no other way of instantiating this darn inner class. But, don't lose hope just yet. We can actually instantiate an inner class outside the actual outer class within which it is coded.

Creating an Inner Class Object from Outside the Outer Class Code

If we want to create an instance of the inner class, we must have an instance of the outer class. You already know that, but think about the implications...it means that, without a reference to an instance of the outer class, you can’t instantiate the inner class from a static method of the outer class (because, don’t forget, in static code there is no this reference), or from any other code in any other class. Inner class instances are always handed an implicit reference to the outer class. The compiler takes care of it, so you’ll never see anything but the end result; the ability of the inner class to access members of the outer class. The code to make an instance from anywhere outside nonstatic code of the outer class is simple.

public static void main(String[] args) {
ExampleOuterClass oc = new ExampleOuterClass();
ExampleOuterClass.ExampleInnerClass ic = oc.new ExampleInnerClass();
ic.accessOuter();
}

The preceding code is the same regardless of whether the main() method is within the ExampleOuterClass class or some other class (assuming the other class has access to ExampleOuterClass, and since ExampleOuterClass has default access, that means the code must be in a class within the same package as ExampleOuterClass).

If you’re into one-liners, you can do it like this:
public static void main(String[] args) {
ExampleOuterClass.ExampleInnerClass inner = new ExampleOuterClass().new ExampleInnerClass();
inner.accessOuter();
}

You can think of this as though you’re invoking a method on the outer instance, but the method happens to be a special inner class instantiation method, and it’s invoked using the keyword new. Instantiating an inner class is the only scenario in which you’ll invoke new on an instance as opposed to invoking new to construct an instance.

Here’s a quick summary of the differences between inner class instantiation code that’s within the outer class and inner class instantiation code that’s outside the outer class:
• From inside the outer class instance code, use the inner class name in the normal way:
ExampleInnerClass mi = new ExampleInnerClass();
• From outside the outer class instance code (including static method code within the outer class), the inner class name must now include the outer class’s name:
ExampleOuterClass.ExampleInnerClass

To instantiate it, you must use a reference to the outer class:
new ExampleOuterClass().new ExampleInnerClass(); or outerObjRef.new ExampleInnerClass();
if you already have an instance of the outer class.

Referencing the Inner or Outer Instance from Within the Inner Class
How does an object refer to itself normally? By using the “this” reference. Here is a quick review of “this” keyword and its features:
• The keyword this can be used only from within instance code.
In other words, not within static code.
• The this reference is a reference to the currently executing object. In other words, the object whose reference was used to invoke the currently running method.
• The this reference is the way an object can pass a reference to itself to some other code, as a method argument:

public void myExMethod() {
MyExClass mc = new MyExClass();
mc.doSomething(this); // pass a ref to object running myExMethod
}

Within an inner class code, the this reference refers to the instance of the inner class, as you’d probably expect, since this always refers to the currently executing object. But what if the inner class code wants an explicit reference to the outer class instance that the inner instance is tied to? In other words, how do you reference the “outer this”? Although normally the inner class code doesn’t need a reference to the outer class, since it already has an implicit one it’s using to access the members of the outer class, it would need a reference to the outer class if it needed to pass that reference to some other code as follows:

class ExampleInnerClass {
public void accessOuter() {
System.out.println("Outer x is " + x);
System.out.println("Inner ref is " + this);
System.out.println("Outer ref is " + ExampleOuterClass.this);
}
}

If we run the complete code as follows:
class ExampleOuterClass {
private int x = 7;
public void instantiateInnerClass() {
ExampleInnerClass in = new ExampleInnerClass();
in.accessOuter();
}
class ExampleInnerClass {
public void accessOuter() {
System.out.println("Outer x is " + x);
System.out.println("Inner ref is " + this);
System.out.println("Outer ref is " + ExampleOuterClass.this);
}
}
public static void main (String[] args) {
ExampleOuterClass.ExampleInnerClass inner = new ExampleOuterClass().new ExampleInnerClass();
inner.accessOuter();
}
}

the output is something like this:
Outer x is 7
Inner ref is ExampleOuterClass$ExampleInnerClass@113708
Outer ref is ExampleOuterClass@33f1d7
So the rules for an inner class referencing itself or the outer instance are as follows:
• To reference the inner class instance itself, from within the inner class code, use this.
• To reference the “outer this” (the outer class instance) from within the inner class code, use NameOfOuterClass.this (example, ExampleOuterClass.this).
Member Modifiers Applied to Inner Classes
A regular inner class is a member of the outer class just as instance variables and methods are, so the following modifiers can be applied to an inner class:
• final
• abstract
• public
• private
• protected
• static—but static turns it into a static nested class not an inner class
• strictfp

Previous Chapter: Self Test - Chapters 38 to 51

Next chapter: Chapter 53 - Method Local Inner Classes

No comments:

Post a Comment

© 2013 by www.inheritingjava.blogspot.com. All rights reserved. No part of this blog or its contents may be reproduced or transmitted in any form or by any means, electronic, mechanical, photocopying, recording, or otherwise, without prior written permission of the Author.

ShareThis

Google+ Followers

Followers