The following code includes a new mechanism for defining concrete subclasses, called anonymous classes, that we have not discussed before. We will explain anonymous classes in more generality later. In this example, anonymous classes are used to generate instances of new subclasses of the interface EntryOperation; the static fields address and phone are bound to objects of type EntryOperation that define the execute method as the method extracting the address and phone fields, respectively, of an Entry.
interface EntryOperation { String execute(Entry e); // implicity public and abstract } abstract class DeptDirectory { ... abstract String findField(EntryOperation c, String name); String findAddress(String name) { return findField(opAddress, name); } String findPhone(String name) { return findField(opPhone, name); static EntryOperation opAddress = new EntryOperation() { // ANONYMOUS class public String execute(Entry e) { return e.getAddress(); } } static EntryOperation opPhone = new EntryOperation() { // ANONYMOUS class public String execute(Entry e) { return e.getPhone(); } } } class Empty extends DeptDirectory { ... String findField(EntryOperation c, String name) { return null; } } class Cons extends DeptDirectory { ... String findField(EntryOperation c, String name) { if (name.equals(first.name)) return c.execute(first); else return rest.findField(c,name); } }
Each brace construction
following a new EntryOperation() expression above defines a unique instance of a new anonymous (unnamed) class implementing the interface EntryOperation. In Java, anonymous classes are simply an abbreviation mechanism. The EntryOperation class could have been written without anonymous classes as follows:{ // ANONYMOUS CLASS public String execute(Entry e) { return e. ...; } }
at the cost of introducing the new class names AddressOperation and PhoneOperation.interface EntryOperation { String execute(Entry e); } } class AddressOperation implements EntryOperation { public String execute(Entry e) { return e.getOffice(); } } class PhoneOperation implements EntryOperation { public String execute(Entry e) { return e.getPhone(); } } abstract class DeptDirectory { ... static EntryOperation opAddress = new AddressOperation(); static EntryOperation opPhone = new PhoneOperation(); }
In general, a single instance of a new class extending class (implementing interface) C can be created using the notation:
new C(...) { ... members ...}where C(...) specifies what superclass initialization should be performed on the instance. If C is an interface, then the argument list in C(...) must be empty. No constructors can appear in the list of members because the class is nameless and cannot be instantiated again. Any required initialization of fields inside the instance can be specified directly in the code defining the class.
If we ignore the ugly notation, an anonymous class extending the abstract class EntryOperation has a direct analog in Scheme that you may have recognized, namely a lambda-expression. In any situation in Scheme where it is appropriate to use a lambda-expression, you can use an anonymous class in Java! The failure to make such an identification is the single most glaring failure of most expositions on Java. Of course, they are typically written for readers with a background in C or C++ rather than Scheme or other language that support procedures as general data objects.
If an anonymous class appears inside a dynamic method, it can contain references to the fields of the enclosing class instance--akin to the free variables that can appear in Scheme lambda-expressions. The only complication is the treatment of the variable this. Since an anonymous class defines an instance of a new class, the variable this inside an anonymous class refers to the new class instance. It does not refer to the enclosing class instance. To refer to the ``entire'' enclosing class instance, Java uses the notation C.this where C is the name of the enclosing class.
Finger Exercise: Modify your solutions to the exercises from the
previous section to use anonymous classes.