7.5 Anonymous Classes

Classes are usually first defined and then instantiated using the new operator. Anonymous classes combine the process of definition and instantiation into a single step. Anonymous classes are defined at the location they are instantiated, using additional syntax with the new operator. As these classes do not have a name, an instance of the class can only be created together with the definition.

An anonymous class can be defined and instantiated in contexts where a reference can be used (i.e., as expressions that evaluate to a reference denoting an object). Anonymous classes are typically used for creating objects on the fly in contexts such as the value in a return statement, an argument in a method call, or in initialization of variables. Anonymous classes are heavily used to implement event listeners in GUI-based applications.

Like local classes, anonymous classes can be defined in static or non-static context. The keyword static is never used.

Extending an Existing Class

The following syntax can be used for defining and instantiating an anonymous class that extends an existing class specified by <superclass name>:


new <superclass name> (<optional argument list>) { <member declarations> }

Optional arguments can be specified, which are passed to the superclass constructor. Thus, the superclass must provide a constructor corresponding to the arguments passed. No extends clause is used in the construct. Since an anonymous class cannot define constructors (as it does not have a name), an instance initializer can be used to achieve the same effect as a constructor. Only non-static members and final static fields can be declared in the class body.

Example 7.11 Defining Anonymous Classes
interface IDrawable {                           // (1)
    void draw();
}
class Shape implements IDrawable {              // (2)
    public void draw() { System.out.println("Drawing a Shape."); }
}

class Painter {                                 // (3) Top-level Class

    public Shape createShape() {                // (4) Non-static Method
        return new Shape(){                     // (5) Extends superclass at (2)
            public void draw() { System.out.println("Drawing a new Shape."); }
        };
    }
    public static IDrawable createIDrawable() { // (7) Static Method
        return new IDrawable(){                 // (8) Implements interface at (1)
            public void draw() {
                System.out.println("Drawing a new IDrawable.");
            }
        };
    }
}

public class AnonClassClient {
    public static void main(String[] args) { // (9)
        IDrawable[] drawables = {            // (10)
            new Painter().createShape(),     // (11) non-static anonymous class
            Painter.createIDrawable(),       // (12) static anonymous class
            new Painter().createIDrawable()  // (13) static anonymous class
        };
        for (int i = 0; i < drawables.length; i++)     // (14)
            drawables[i].draw();

        System.out.println("Anonymous Class Names:");
        System.out.println(drawables[0].getClass());   // (15)
        System.out.println(drawables[1].getClass());   // (16)
    }
}

Output from the program:

Drawing a new Shape.
Drawing a new IDrawable.
Drawing a new IDrawable.
Anonymous Class Names:
class Painter$1
class Painter$2

Class definitions from Example 7.11, an adaptation of Example 7.10 to anonymous classes, are shown below. The non-static method createShape() at (4) defines a non-static anonymous class at (5), which extends the superclass Shape. The anonymous class overrides the inherited method draw(). As references to an anonymous class cannot be declared, the functionality of the class is only available through superclass references. Usually it makes sense to override methods from the superclass. Any other members declared in an anonymous class cannot be accessed by an external client.

// ...
class Shape implements IDrawable {          // (2)
    public void draw() { System.out.println("Drawing a Shape."); }
}

class Painter {                             // (3) Top-level Class

    public Shape createShape() {            // (4) Non-static Method
        return new Shape() {                // (5) Extends superclass at (2)
            public void draw() { System.out.println("Drawing a new Shape."); }
        };
    }
    // ...

}
// ...

Implementing an Interface

The following syntax can be used for defining and instantiating an anonymous class that implements an interface specified by <interface name>:


new <interface name>() { <member declarations> }

An anonymous class provides a single interface implementation, and no arguments are passed. The anonymous class implicitly extends the Object class. Note that no implements clause is used in the construct. The class body has the same restrictions as previously noted for anonymous classes extending an existing class.

An anonymous class implementing an interface is shown below. Details can be found in Example 7.11. The static method createIDrawable() at (7) defines a static anonymous class at (8), which implements the interface IDrawable, by providing an implementation of the method draw(). The functionality of objects of an anonymous class that implements an interface is available through references of the interface type and the Object type, (i.e., the supertypes).

interface IDrawable {                           // (1) Interface
    void draw();
}
// ...
class Painter {                                 // (3) Top-level Class
    // ...
    public static IDrawable createIDrawable() { // (7) Static Method
        return new IDrawable(){                 // (8) Implements interface at (1)
            public void draw() {
                System.out.println("Drawing a new IDrawable.");
            }
        };
    }
}
// ...

The following code is an example of a typical use of anonymous classes in building GUI-application. The anonymous class at (1) implements the ActionListener interface that has the method actionPerformed(). When the addActionListener() method is called on the GUI-button denoted by the reference quitButton, the anonymous class is instantiated and the object reference value passed as a parameter to the method. The method addActionListener() of the GUI-button can use the reference value to invoke the method actionPerformed() in the ActionListener object.

quitButton.addActionListener(
        new ActionListener() {    // (1) Anonymous class implements interface.
            // Invoked when the user clicks the quit button.
            public void actionPerformed(ActionEvent evt) {
                System.exit(0);   // (2) Terminates the program.
            }
        }
);

Instantiating Anonymous Classes

The discussion on instantiating local classes (see Example 7.10) is also valid for instantiating anonymous classes. The class AnonClassClient in Example 7.11 creates one instance at (11) of the non-static anonymous class defined at (5), and two instances at (12) and (13) of the static anonymous class defined at (8). The program output shows the polymorphic behavior and the runtime types of the objects. Similar to a non-static local class, an instance of a non-static anonymous class has an instance of its enclosing class at (11). An enclosing instance is not mandatory for creating objects of a static anonymous class, as shown at (12).

The names of the anonymous classes at runtime are also shown in the program output in Example 7.11. They are also the names used to designate their respective class files. Anonymous classes are not so anonymous after all.

Accessing Declarations in Enclosing Context

Access rules for local classes (see Section 7.4, p. 304) also apply to anonymous classes. Example 7.12 is an adaptation of Example 7.9 and illustrates the access rules for anonymous classes. The local classes in Example 7.9 have been adapted to anonymous classes in Example 7.12. The TLCWithAnonClasses class has two methods: one non-static and the other static, which return an instance of a non-static and a static anonymous class, respectively. Both anonymous classes extend the Base class.

Anonymous classes can access final variables only in the enclosing context. Inside the definition of a non-static anonymous class, members of the enclosing context can be referenced using the <enclosing class name>.this construct. Non-static anonymous classes can also access any non-hidden members by their simple names in the enclosing context, whereas static anonymous classes can only access non-hidden static members.

Example 7.12 Accessing Declarations in Enclosing Context
class Base {
    protected int nsf1;
}

class TLCWithAnonClasses {               // Top level Class
    private double nsf1;                 // Non-static field
    private int    nsf2;                 // Non-static field
    private static int sf;               // Static field

    Base nonStaticMethod(final int fp) { // Non-static Method
        final int flv  = 10;             // final local variable
        final int hlv  = 30;             // final (hidden) local variable
              int nflv = 20;             // non-final local variable
        return new Base() {           // Non-static anonymous class
        //        static int f1;      // (1) Not OK. Static members not allowed.
            final static int f2 = 10; // (2) final static members allowed.
            int    f3  = fp;    // (3) final param from enclosing method.
            int    f4  = flv;   // (4) final local var from enclosing method.
        //  double f5  = nflv;  // (5) Not OK. Only finals from enclosing method.
            double f6  = nsf1;        // (6) Inherited from superclass.
            double f6a = this.nsf1;   // (6a) Inherited from superclass.
            double f6b = super.nsf1;  // (6b) Inherited from superclass.
            double f7  = TLCWithAnonClasses.this.nsf1; // (7) In enclosing object.
            int    f8  = nsf2;        // (8)  In enclosing object.
            int    f9  = sf;          // (9)  static from enclosing class.
            int    hlv;               // (10) Hides local variable.
        };
    }

    static Base staticMethod(final int fp) { // Static Method
        final int flv  = 10;                 // final local variable
        final int hlv  = 30;                 // final (hidden) local variable
              int nflv = 20;                 // non-final local variable

        return new Base() {           // Static anonymous class
        //        static int f1;      // (11) Not OK. Static members not allowed.
            final static int f2 = 10; // (12) final static members allowed.
            int    f3  = fp;    // (13) final param from enclosing method.
            int    f4  = flv;   // (14) final local var from enclosing method.
        //  double f5  = nflv;  // (15) Not OK. Only finals from enclosing method.
            double f6  = nsf1;        // (16 ) Inherited from superclass.
            double f6a = this.nsf1;   // (16a) Inherited from superclass.
            double f6b = super.nsf1;  // (16b) Inherited from superclass.
        //  double f7  = TLCWithAnonClasses.this.nsf1; //(17) No enclosing object.
        //  int    f8  = nsf2;        // (18)  No enclosing object.
            int    f9  = sf;          // (19)  static from enclosing class.
            int    hlv;               // (20) Hides local variable.
        };
    }
}