Certification Objective Inner Classes


Certification Objective —Inner Classes

You're an OO 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.

Event handlers are perhaps the best example of this (and are, in fact, one of the main reasons inner classes were added to the language in the first place). If you have a GUI class that performs some job like, say, a chat client, you might want the chat-client-specific methods (accept input, read new messages from server, send user input back to server, and so on) to be in the class. But how do those methods get invoked in the first place? A user clicks a button. Or types some text in the input field. Or a separate thread doing the I/O work of getting messages from the server has messages that need to be displayed in the GUI. So you have chat-client—specific methods, but you also need methods for handling the "events" (button presses, keyboard typing, I/O available, and so on) that drive the calls on those chat-client methods. The ideal scenario—from an OO perspective—is to keep the chat-client—specific methods in the ChatClient class, and put the event-handling code in a separate event-handling class.

Nothing unusual about that so far; after all, that's how you're supposed to design OO classes. As specialists. But here's the problem with the chat-client scenario: the event-handling code is intimately tied to the chat-client-specific code! Think about it: when the user presses a Send button (indicating that they want their typed-in message to be sent to the chat server), the chat-client code that sends the message needs to read from a particular text field. In other words, if the user clicks Button A, the program is supposed to extract the text from the TextField B, of a particular ChatClient instance. Not from some other text field from some other object, but specifically the text field that a specific instance of the ChatClient class has a reference to. So the event-handling code needs access to the members of the ChatClient object, to be useful as a "helper" to a particular ChatClient instance.

And what if the ChatClient class needs to inherit from one class, but the event handling code is better off inheriting from some other class? You can't make a class extend more than one class, so putting all the code (the chat-client—specific code and the event-handling code) in one class won't work in that case. So what you'd really like to have is the benefit of putting your event code in a separate class (better OO, encapsulation, and the ability to extend a class other than the class the ChatClient extends) but still allow the event-handling code to have easy access to the members of the ChatClient (so the event-handling code can, for example, update the ChatClient's private instance variables). You could manage it by making the members of the ChatClient accessible to the event-handling class by, for example, marking them public. But that's not a good solution either.

You already know where this is going—one of the key benefits of an inner class is the "special relationship" an inner class instance shares with an instance of the outer class. That "special relationship" gives code in the inner class access to members of the enclosing (outer) class, as if the inner class were part of the outer class. In fact, that's exactly what it means: the inner class is a part of the outer class. Not just a "part" but a full-fledged, card-carrying member of the outer class. Yes, an inner class instance has access to all members of the outer class, even those marked private. (Relax, that's the whole point, remember? We want this separate inner class instance to have an intimate relationship with the outer class instance, but we still want to keep everyone else out. And besides, if you wrote the outer class, then you also wrote the inner class! So you're not violating encapsulation; you designed it this way.)

Coding a "Regular" Inner Class

We use the term regular here to represent inner classes that are not:

  • Static

  • Method-local

  • Anonymous

For the rest of this section, though, we'll just use the term "inner class" and drop the "regular". (When we switch to one of the other three types in the preceding list, you'll know it.) You define an inner class within the curly braces of the outer class:

 class MyOuter {    class MyInner { } } 

Piece of cake. And if you compile it,

 %javac MyOuter.java 

you'll end up with two class files:

  • MyOuter.class

  • MyOuter$MyInner.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 MyOuter$MyInner 

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. You'll see all this in a moment. First, let's beef up the classes a little:

 class MyOuter {    private int x = 7;    // inner class definition    class MyInner {       public void seeOuter() {          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 (say, an instance method) can access any other member of the outer class, private or not, the inner class—also a member—can do the same.

OK, so now that we know how to write the code giving an inner class access to members of the outer class, how do you actually use it?

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.

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 personal use. We'll modify the MyOuter class to create an instance of MyInner:

 class MyOuter {    private int x = 7;    public void makeInner() {       MyInner in = new MyInner();  // make an inner instance       in.seeOuter();    }    class MyInner {       public void seeOuter() {          System.out.println("Outer x is " + x);       }    } } 

You can see in the preceding code that the MyOuter code treats Mylnner just as though MyInner were any other accessible class—it instantiates it using the class name (new MyInner()), and then invokes a method on the reference variable (in.seeOuter()). 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 classthe instance running the makeInner() method. So how do you instantiate a MyInner object from somewhere outside the MyOuter class? Is it even possible? (Well, since we're going to all the trouble of making a whole new subhead for it, as you'll see next, there's no big mystery here.)

Creating an Inner Class Object from Outside the Outer Class Instance Code Whew. Long subhead there, but it does explain what we're trying to do. 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 implicationsit 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 non-static code of the outer class is simple, but you must memorize this for the exam!

 public static void main(String[] args) {   MyOuter mo = new MyOuter();     // gotta get an instance!   MyOuter.MyInner inner = mo.new MyInner();   inner.seeOuter(); } 

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

If you're into one-liners, you can do it like this:

 public static void main(String[] args) {   MyOuter.MyInner inner = new MyOuter().new MyInner();   inner.seeOuter(); } 

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 (but not static), 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:

     MyInner mi = new MyInner(); 

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

     MyOuter.MyInner 

    To instantiate it, you must use a reference to the outer class:

     new MyOuter().new MyInner.(); or outerObjRef.new MyInner(); 

    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:

  • 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 myMethod() {   MyClass mc = new MyClass();   mc.doStuff(this);  // pass a ref to object running myMethod } 

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 MyInner {    public void seeOuter() {       System.out.println("Outer x is " + x);       System.out.println("Inner class ref is " + this);       System.out.println("Outer class ref is " + MyOuter.this);    } } 

If we run the complete code as follows:

 class MyOuter {    private int x = 7;    public void makeInner() {       MyInner in = new MyInner();       in.seeOuter();    }    class MyInner       public void seeOuter() {          System.out.println("Outer x is " + x);          System.out.println("Inner class ref is " + this);          System.out.println("Outer class ref is " + MyOuter.this);       }    }    public static void main (String[] args) {       MyOuter.MyInner inner = new MyOuter().new MyInner();       inner.seeOuter();    } } 

the output is something like this:

 Outer x is 7 Inner class ref is MyOuter$MyInner@113708 Outer class ref is MyOuter@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, MyOuter.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

  • staticbut static turns it into a static nested class not an inner class.

  • strictfp




SCJP Sun Certified Programmer for Java 5 Study Guide Exam 310-055
SCJP Sun Certified Programmer for Java 5 Study Guide (Exam 310-055) (Certification Press)
ISBN: 0072253606
EAN: 2147483647
Year: 2006
Pages: 131

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net