Certification Objective Anonymous Inner Classes


Certification Objective —Anonymous Inner Classes

So far we've looked at defining a class within an enclosing class (a regular inner class) and within a method (a method-local inner class). Finally, we're going to look at the most unusual syntax you might ever see in Java; inner classes declared without any class name at all (hence the word anonymous). And if that's not weird enough, you can define these classes not just within a method, but even within an argument to a method. We'll look first at the plain-old (as if there is such a thing as a plain-old anonymous inner class) version (actually, even the plain-old version comes in two flavors), and then at the argument-declared anonymous inner class.

Perhaps your most important job here is to learn to not be thrown when you see the syntax. The exam is littered with anonymous inner class code: you might see it on questions about threads, wrappers, overriding, garbage collection, andwell, you get the idea.

Plain-Old Anonymous Inner Classes, Flavor One

Check out the following legal-but-strange-the-first-time-you-see-it code:

 class Popcorn {    public void pop() {       System.out.println("popcorn");     } } class Food {    Popcorn p = new Popcorn() {      public void pop() {         System.out.println("anonymous popcorn");      }    }; } 

Let's look at what's in the preceding code:

  • We define two classes, Popcorn and Food.

  • Popcorn has one method, pop().

  • Food has one instance variable, declared as type Popcorn. That's it for Food. Food has no methods.

And here's the big thing to get:

The Popcorn reference variable refers not to an instance of Popcorn, but to an instance of an anonymous (unnamed) subclass of Popcorn.

Let's look at just the anonymous class code:

 2. Popcorn p = new Popcorn() { 3.   public void pop() { 4.      System.out.println("anonymous popcorn"); 5.   } 6. }; 

Line 2 Line 2 starts out as an instance variable declaration of type Popcorn. But instead of looking like this:

 Popcorn p = new Popcorn(); //notice the semicolon at the end 

there's a curly brace at the end of line 2, where a semicolon would normally be.

 Popcorn p = new Popcorn() { // a curly brace, not a semicolon 

You can read line 2 as saying,

Declare a reference variable, p, of type Popcorn. Then declare a new class that has no name, but that is a subclass of Popcorn. And here's the curly brace that opens the class definition

Line 3 Line 3, then, is actually the first statement within the new class definition. And what is it doing? Overriding the pop() method of the superclass Popcorn. This is the whole point of making an anonymous inner class—to override one or more methods of the superclass! (Or to implement methods of an interface, but we'll save that for a little later.)

Line 4 Line 4 is the first (and in this case only) statement within the overriding pop() method. Nothing special there.

Line 5 Line 5 is the closing curly brace of the pop() method. Nothing special.

Line 6 Here's where you have to pay attention: line 6 includes a curly brace closing off the anonymous class definition (it's the companion brace to the one on line 2), but there's more! Line 6 also has the semicolon that ends the statement started on line 2—the statement where it all began—the statement declaring and initializing the Popcorn reference variable. And what you're left with is a Popcorn reference to a brand-new instance of a brand-new, just-in-time, anonymous (no name) subclass of Popcorn.

image from book
Exam Watch

The closing semicolon is hard to spot. Watch for code like this:

 2. Popcorn p = new Popcorn() { 3.   public void pop() { 4.      System.out.println("anonymous popcorn"); 5.   } 6. }                 //  Missing the semicolon needed to end                      //  the statement started on 2! 7. Foo f = new Foo(); 

You'll need to be especially careful about the syntax when inner classes are involved, because the code on line 6 looks perfectly natural. We're not used to seeing semicolons following curly braces (the only other time it happens is with shortcut array Initializations).

image from book

Polymorphism is in play when anonymous inner classes are involved. Remember that, as in the preceding Popcorn example, we're using a superclass reference variable type to refer to a subclass object. What are the implications? You can only call methods on an anonymous inner class reference that are defined in the reference variable type! This is no different from any other polymorphic references, for example,

 class Horse extends Animal{    void buck() { } } class Animal {    void eat() { } } class Test {    public static void main (String[] args) {       Animal h = new Horse();       h.eat();  // Legal, class Animal has an eat() method       h.buck();  // Not legal! Class Animal doesn't have buck()    } } 

So on the exam, you must be able to spot an anonymous inner class that—rather than overriding a method of the superclass—defines its own new method. The method definition isn't the problem, though; the real issue is how do you invoke that new method? The reference variable type (the superclass) won't know anything about that new method (defined in the anonymous subclass), so the compiler will complain if you try to invoke any method on an anonymous inner class reference that is not in the superclass class definition.

Check out the following, illegal code:

 class Popcorn {    public void pop() {       System.out.println("popcorn");    } } class Food {    Popcorn p = new Popcorn() {       public void sizzle () {         System.out.println("anonymous sizzling popcorn");       }       public void pop() {          System.out.println("anonymous popcorn");       }    };    public void popIt() {       p.pop();     // OK, Popcorn has a pop() method       p.sizzle();  // Not Legal! Popcorn does not have sizzle()    } } 

Compiling the preceding code gives us something like,

 Anon.Java:19: cannot resolve symbol symbol  : method sizzle  () location: class Popcorn       p.sizzle();        ^ 

which is the compiler's way of saying, "I can't find method sizzle() in class Popcorn," followed by, "Get a clue."

Plain-Old Anonymous Inner Classes, Flavor Two

The only difference between flavor one and flavor two is that flavor one creates an anonymous subclass of the specified class type, whereas flavor two creates an anonymous implementer of the specified interface type. In the previous examples, we defined a new anonymous subclass of type Popcorn as follows:

 Popcorn p = new Popcorn() { 

But if Popcorn were an interface type instead of a class type, then the new anonymous class would be an implementer of the interface rather than a subclass of the class. Look at the following example:

 interface Cookable {    public void cook(); } class Food {    Cookable c = new Cookable() {      public void cook() {         System.out.println("anonymous cookable implementer");      }    }; } 

The preceding code, like the Popcorn example, still creates an instance of an anonymous inner class, but this time the new just-in-time class is an implementer of the Cookable interface. And note that this is the only time you will ever see the syntax

 new Cookable() 

where Cookable is an interface rather than a non-abstract class type. Because think about it, you can't instantiate an interface, yet that's what the code looks like it's doing. But of course it's not instantiating a Cookable object, it's creating an instance of a new, anonymous, implementer of Cookable. You can read this line:

 Cookable c = new Cookable() { 

as, "Declare a reference variable of type Cookable that, obviously, will refer to an object from a class that implements the Cookable interface. But, oh yes, we don't yet have a class that implements Cookable, so we're going to make one right here, right now. We don't need a name for the class, but it will be a class that implements Cookable, and this curly brace starts the definition of the new implementing class."

One more thing to keep in mind about anonymous interface implementers-they can implement only one interface. There simply isn't any mechanism to say that your anonymous inner class is going to implement multiple interfaces. In fact, an anonymous inner class can't even extend a class and implement an interface at the same time. The inner class has to choose either to be a subclass of a named class—and not directly implement any interfaces at all—or to implement a single interface. By directly, we mean actually using the keyword implements as part of the class declaration. If the anonymous inner class is a subclass of a class type, it automatically becomes an implernenter of any interfaces implemented by the superclass.

image from book
Exam Watch

Don't be fooled by any attempts to instantiate an interface except in the case of an anonymous inner class. The following is not legal,

 Runnable r = new Runnable (); // can't instantiate interface 

whereas the following is legal, because it's instantiating an implementer of the Runnable interface (an anonymous implementation class):

 Runnable r = new Runnable() {  // curly brace, not semicolon   public void run() { } }; 

image from book

Argument-Defined Anonymous Inner Classes

If you understood what we've covered so far in this chapter, then this last part will be simple. If you are still a little fuzzy on anonymous classes, however, then you should reread the previous sections. If they're not completely clear, we'd like to take full responsibility for the confusion. But we'll be happy to share.

Okay, if you've made it to this sentence, then we're all going to assume you understood the preceding section, and now we're just going to add one new twist. Imagine the following scenario. You're typing along, creating the Perfect Class, when you write code calling a method on a Bar object, and that method takes an object of type Foo (an interface).

 class MyWonderfulClass {    void go() {       Bar b = new Bar();       b.doStuff(ackWeDoNotHaveAFoo!); // Don't try to compile this at home    } } interface Foo {    void foof(); } class Bar {    void doStuff(Foo f) { } } 

No problemo, except that you don't have an object from a class that implements Foo, and you can't instantiate one, either, because you don't even have a class that implements Foo, let alone an instance of one. So you first need a class that implements Foo, and then you need an instance of that class to pass to the Bar class's doStuff() method. Savvy Java programmer that you are, you simply define an anonymous inner class, right inside the argument. That's right, just where you least expect to find a class. And here's what it looks like:

  1. class MyWonderfulClass {  2.   void go() {  3.     Bar b = new Bar();  4.     b.doStuff(new Foo() {  5.       public void foof() {  6.         System.out.println("foofy");  7.       } // end foof method  8.     }); // end inner class def, arg, and b.doStuff stmt.  9.   } // end go() 10. } // end class 11. 12. interface Foo { 13.   void foof(); 14. } 15. class Bar { 16.   void doStuff(Foo f) {} 17. } 

All the action starts on line 4. We're calling doStuff() on a Bar object, but the method takes an instance that IS-A Foo, where Foo is an interface. So we must make both an implementation class and an instance of that class, all right here in the argument to dostuff(). So that's what we do. We write

 new Foo() { 

to start the new class definition for the anonymous class that implements the Foo interface. Foo has a single method to implement, foof(), so on lines 5, 6, and 7 we implement the foof() method. Then on line 8—whoa!—more strange syntax appears. The first curly brace closes off the new anonymous class definition. But don't forget that this all happened as part of a method argument, so the close parenthesis, ), finishes off the method invocation, and then we must still end the statement that began on line 4, so we end with a semicolon. Study this syntax! You will see anonymous inner classes on the exam, and you'll have to be very, very picky about the way they're closed. If they're argument local, they end like this:

 }); 

but if they're just plain-old anonymous classes, then they end like this:

 }; 

Regardless, the syntax is not what you use in virtually any other part of Java, so be careful. Any question from any part of the exam might involve anonymous inner classes as part of the code.




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