Chapter 6. Relationships Among Classes

So far in our exploration of Java, we have seen how to create Java classes and objects, which are instances of those classes. By themselves, classes would be little more than a convention for organizing code. It is in the relationships between objects—their connections and privileges with respect to one another—that the power of an object-oriented language is really expressed.

That’s what we’ll cover in this chapter. In particular, we’ll look at several kinds of relationships:

Inheritance relationships

How a class inherits methods and variables from its parent class

Interfaces

How to declare that a class implements certain behavior and define a type to refer to that behavior

Packaging

How to organize objects into logical groups

Inner classes

A generalization of classes that lets you nest a class definition inside another class definition

Subclassing and Inheritance

Classes in Java exist in a hierarchy. A class in Java can be declared as a subclass of another class using the extends keyword. A subclass inherits variables and methods from its superclass and can use them as if they were declared within the subclass itself:

    class Animal {
        float weight;
        ...
        void eat() {
            ...
        }
        ...
    }

    class Mammal extends Animal {
        // inherits weight
        int heartRate;
        ...

        // inherits eat()
        void breathe() {
            ...
        }
    }

In this example, an object of type Mammal has both the instance variable weight and the method eat(). They are inherited from Animal.

A class can extend only one other class. To use the proper terminology, Java allows single inheritance of class implementation. Later in this chapter, we’ll talk about interfaces, which take the place of multiple inheritance as it’s primarily used in other languages.

A subclass can be further subclassed. Normally, subclassing specializes or refines a class by adding variables and methods (you cannot remove or hide variables or methods by subclassing). For example:

    class Cat extends Mammal {
        // inherits weight and heartRate
        boolean longHair;
        ...

        // inherits eat() and breathe()
        void purr() {
            ...
        }
    }

The Cat class is a type of Mammal that is ultimately a type of Animal. Cat objects inherit all the characteristics of Mammal objects and, in turn, Animal objects. Cat also provides additional behavior in the form of the purr() method and the longHair variable. We can denote the class relationship in a diagram, as shown in Figure 6-1.

A subclass inherits all members of its superclass not designated as private. As we’ll discuss shortly, other levels of visibility affect which inherited members of the class can be seen from outside of the class and its subclasses, but at a minimum, a subclass always has the same set of visible members as its parent. For this reason, the type of a subclass can be considered a subtype of its parent, and instances of the subtype can be used anywhere instances of the supertype are allowed. Consider the following example:

    Cat simon = new Cat();
    Animal creature = simon;
A class hierarchy
Figure 6-1. A class hierarchy

The Cat instance simon in this example can be assigned to the Animal type variable creature because Cat is a subtype of Animal. Similarly, any method accepting an Animal object would accept an instance of a Cat or any Mammal type as well. This is an important aspect of polymorphism in an object-oriented language such as Java. We’ll see how it can be used to refine a class’s behavior, as well as add new capabilities to it.

Shadowed Variables

In Chapter 5, we saw that a local variable of the same name as an instance variable shadows (hides) the instance variable. Similarly, an instance variable in a subclass can shadow an instance variable of the same name in its parent class, as shown in Figure 6-2. We’re going to cover the details of this variable hiding now for completeness and in preparation for more advanced topics, but in practice you should almost never do this. It is much better in practice to structure your code to clearly differentiate variables using different names or naming conventions.

In Figure 6-2, the variable weight is declared in three places: as a local variable in the method foodConsumption() of the class Mammal, as an instance variable of the class Mammal, and as an instance variable of the class Animal. The actual variable selected when you reference it in the code would depend on the scope in which we are working and how you qualify the reference to it.

The scope of shadowed variables
Figure 6-2. The scope of shadowed variables

In the previous example, all variables were of the same type. A slightly more plausible use of shadowed variables would involve changing their types. We could, for example, shadow an int variable with a double variable in a subclass that needs decimal values instead of integer values. We can do this without changing the existing code because, as its name suggests, when we shadow variables, we don’t replace them but instead mask them. Both variables still exist; methods of the superclass see the original variable, and methods of the subclass see the new version. The determination of what variables the various methods see occurs at compile time.

Here’s a simple example:

    class IntegerCalculator {
        int sum;
        ...
    }

    class DecimalCalculator extends IntegerCalculator {
        double sum;
        ...
    }

In this example, we shadow the instance variable sum to change its type from int to double.[15] Methods defined in the class IntegerCalculator see the integer variable sum, while methods defined in DecimalCalculator see the floating-point variable sum. However, both variables actually exist for a given instance of DecimalCalculator, and they can have independent values. In fact, any methods that DecimalCalculator inherits from IntegerCalculator actually see the integer variable sum.

Because both variables exist in DecimalCalculator, we need a way to reference the variable inherited from IntegerCalculator. We do that using the super keyword as a qualifier on the reference:

    int s = super.sum;

Inside of DecimalCalculator, the super keyword used in this manner selects the sum variable defined in the superclass. We’ll explain the use of super more fully in a bit.

Another important point about shadowed variables has to do with how they work when we refer to an object by way of a less derived type (a parent type). For example, we can refer to a DecimalCalculator object as an IntegerCalculator by using it via a variable of type IntegerCalculator. If we do so and then access the variable sum, we get the integer variable, not the decimal one:

    DecimalCalculator dc = new DecimalCalculator();
    IntegerCalculator ic = dc;

    int s = ic.sum;       // accesses IntegerCalculator sum

The same would be true if we accessed the object using an explicit cast to the IntegerCalculator type or when passing an instance into a method that accepts that parent type.

To reiterate, the usefulness of shadowed variables is limited. It’s much better to abstract the use of variables like this in other ways than to use tricky scoping rules. However, it’s important to understand the concepts here before we talk about doing the same thing with methods. We’ll see a different and more dynamic type of behavior when methods shadow other methods, or to use the correct terminology, override other methods.

Overriding Methods

In Chapter 5, we saw that we could declare overloaded methods (i.e., methods with the same name but a different number or type of arguments) within a class. Overloaded method selection works in the way we described on all methods available to a class, including inherited ones. This means that a subclass can define additional overloaded methods that add to the overloaded methods provided by a superclass.

A subclass can do more than that; it can define a method that has exactly the same method signature (name and argument types) as a method in its superclass. In that case, the method in the subclass overrides the method in the superclass and effectively replaces its implementation, as shown in Figure 6-3. Overriding methods to change the behavior of objects is called subtype polymorphism. It’s the usage most people think of when they talk about the power of object-oriented languages.

Method overriding
Figure 6-3. Method overriding

In Figure 6-3, Mammal overrides the reproduce() method of Animal, perhaps to specialize the method for the behavior of mammals giving birth to live young.[16] The Cat object’s sleeping behavior is also overridden to be different from that of a general Animal, perhaps to accommodate cat naps. The Cat class also adds the more unique behaviors of purring and hunting mice.

From what you’ve seen so far, overridden methods probably look like they shadow methods in superclasses, just as variables do. But overridden methods are actually more powerful than that. When there are multiple implementations of a method in the inheritance hierarchy of an object, the one in the “most derived” class (the furthest down the hierarchy) always overrides the others, even if we refer to the object through a reference of one of the superclass types.[17]

For example, if we have a Cat instance assigned to a variable of the more general type Animal, and we call its sleep() method, we still get the sleep() method implemented in the Cat class, not the one in Animal:

    Cat simon = new Cat();
    Animal creature = simon;
      ...
    creature.sleep();       // accesses Cat sleep();

In other words, for purposes of behavior (invoking methods), a Cat acts like a Cat, regardless of whether you refer to it as such. In other respects, the variable creature here may behave like an Animal reference. As we explained earlier, access to a shadowed variable through an Animal reference would find an implementation in the Animal class, not the Cat class. However, because methods are located dynamically, searching subclasses first, the appropriate method in the Cat class is invoked, even though we are treating it more generally as an Animal object. This means that the behavior of objects is dynamic. We can deal with specialized objects as if they were more general types and still take advantage of their specialized implementations of behavior.

@Override

A common programming error in Java is to accidentally overload a method when trying to override it. Any difference in the number or type of arguments (the method signature) produces two overloaded methods instead of a single, overridden method. The new annotations syntax in Java 5.0 provides a way to get the compiler to help with this problem. An annotation, as we’ll describe in Chapter 7, allows us to add special markers or metadata to source code that can be read by the compiler or runtime tools. One of the standard annotations that Java defines is called @Override and it tells the compiler that the method it marks is intended to override a method in the superclass. The compiler then warns if the method doesn’t match. For example, we could specify that the sleep() method of our Cat class overrides one in a superclass like so:

    class Cat extends Mammal {
        ...
        @Override void sleep() { ... }
    }

Overridden methods and dynamic binding

In a previous section, we mentioned that overloaded methods are selected by the compiler at compile time. Overridden methods, on the other hand, are selected dynamically at runtime. Even if we create an instance of a subclass our code has never seen before (perhaps a new class loaded over the network), any overriding methods that it contains are located and used at runtime, replacing those that existed when we last compiled our code.

In contrast, if we created a new class that implements an additional, more specific, overloaded method, and replace the compiled class in our classpath with it, our code would continue to use the implementation it discovered originally. This situation would persist until we recompiled our code along with the new class. Another effect of this is that casting (i.e., explicitly telling the compiler to treat an object as one of its assignable types) affects the selection of overloaded methods at compile time but not overridden methods.

In practice what we’ve just described is not something you need to worry about often, but it’s important in understanding what the virtual machine does and does not do at runtime.

Static method binding

Static methods don’t belong to any object instance; they are accessed directly through a class name, so they are not dynamically selected at runtime like instance methods. That is why static methods are called “static”; they are always bound at compile time.

A static method in a superclass can be shadowed by another static method in a subclass, as long as the original method was not declared final. However, both methods are always accessible directly via their respective class names. You can’t “override” a static method with an instance method. In other words, you can’t have a static method and instance method with the same signature in the same class hierarchy.

final methods and performance

In languages like C++, the default is for methods to act like shadowed variables, so you have to declare explicitly the methods you want to be dynamic (or, as C++ terms them, virtual). In Java, instance methods are, by default, dynamic. But you can use the final modifier to declare that an instance method can’t be overridden in a subclass, and it won’t be subject to dynamic binding.

We have seen final used with variables to effectively make them constants. When applied to a method, final means that its implementation is constant—no overriding allowed. final can also be applied to an entire class, which means the class can’t be subclassed.

In the old days, dynamic method binding came with a significant performance penalty, and some people are still inclined to use the final modifier to guard against this. Modern Java runtime systems eliminate the need for this kind of tweaking. A profiling runtime can determine which methods are not being overridden and “optimistically” inline them, treating them as if they were final until it becomes necessary to do otherwise. As a rule, you should use the final keyword when it is correct for your program’s structure, not for performance considerations.

Compiler optimizations

In some older versions of Java, the javac compiler can be run with a -O switch, which tells it to perform certain optimizations, like inlining, statically. Most of these optimizations are now done at runtime by smarter VMs, so switches like this are generally not necessary.

Another kind of optimization allows you to include debugging code in your Java source without incurring a size or performance penalty. Although Java doesn’t have a preprocessor to explicitly control what source is included, you can get some of the same effects by making a block of code conditional on a constant (i.e., static and final) variable. The Java compiler is smart enough to remove this code when it determines that it won’t be called. For example:

    static final boolean DEBUG = false;
    ...
    final void debug (String message) {
        if (DEBUG) {
            System.err.println(message);
            // do other stuff
            ...
        }
    }

In this case, the compiler can recognize that the condition on the DEBUG variable is always false, and the body of the debug() method will be optimized away. With a modern compiler, the method call might even be optimized away entirely.

Note that this kind of debugging code is useful for purposes such as logging. In contrast to assertions, which we covered in Chapter 4, which are supposed to be yes/no tests that guarantee the correctness of your program logic, these conditional blocks of code might do expensive formatting or other output processing that is useful during develoment but you don’t wish to have around in the final product.

Method selection revisited

By now you should have a good, intuitive feel for how methods are selected from the pool of potentially overloaded and overridden method names of a class. If, however, you are dying for more detail, we’ll provide it now.

In a previous section, we offered an inductive rule for overloaded method resolution. It said that a method is considered more specific than another if its arguments are assignable to the arguments of the second method. We can now expand this rule to include the resolution of overridden methods by adding the following condition: to be more specific than another method, the type of the class containing the method must also be assignable to the type of the class holding the second method.

What does that mean? Well, the only classes whose types are assignable are classes in the same inheritance hierarchy, meaning that we’re talking about the set of all methods of the same name in a class or any of its parent or child classes. Because subclass types are assignable to superclass types, but not vice versa, the resolution is pushed in the way that we expect down the chain toward the subclasses. This effectively adds a second dimension to the search, in which resolution is pushed down the inheritance tree toward more refined classes and, simultaneously, toward the most specific overloaded method within a given class.

Exceptions and overridden methods

An overriding method may change the behavior of an object, but in some ways, it must still fulfill the contract of the original method with the user. Specifically, an overriding method must adhere to the throws clause of the original method. The new method cannot throw new types of checked exceptions. It can only declare that it throws exception types assignable to those thrown by the method in the parent class; that is, it may declare that it throws the same types of exceptions or subtypes of those declared by the original method. If the new method does not throw any of the checked exceptions of the original, it does not have to declare them and callers of the method via the subclass do not have to guard against them. (In this way, you can override a method to “handle” exceptions for the user.)

So the new method may declare exactly the same checked exceptions as the original, or it has the option to refine those types by declaring that it throws more specific subtypes than the overridden method. This is not the same as just saying that the method can simply throw subtypes of its declared exceptions; any method can do that. The new method can actually redefine the throws clause of the method to be more specific. This technique is called covariant typing of the throws clause, which means that the exception types against which the user must guard change to become more refined with the subtype.

Let’s quickly review what the throws clause really means. If a method declares that it can throw an IOException, it is really saying that it can throw exceptions of type IOException or its subtypes. For example, FileNotFoundException is a type of IOException. A method declaring that it can throw IOException could actually throw FileNotFoundException or any other subtype of IOException at runtime:

    public void readFile() throws IOException {
        ...
        if ( error ) throw new FileNotFoundException( filename );
    }

When we call this method, the compiler will ensure that we allow for the possibility of any kind of IOException, using either a try/catch block or by throwing the exception from our own method.

When we override a method in a subclass, we get an opportunity to rewrite the throws clause of the method a bit. The new method must still be backward-compatible with the original, so any checked exceptions it throws must be assignable to those thrown by the overridden method. But we can be more specific if we want, refining the type of exception to go along with the new method’s behavior. For example:

    class MeatInedibleException extends InedibleException { ... }

    class Animal {
        void eat( Food f ) throws InedibleException {
            ...
        }
    }
    class Herbivore extends Animal {
        void eat( Food f ) throws MeatInedibleException {
            if ( f instanceof Meat )
                throw new MeatInedibleException();
            ...
        }
    }

In this code, Animal specifies that it can throw an InedibleException from its eat() method. Herbivore is a subclass of Animal, so its eat() method must also be able to throw an InedibleException. However, Herbivore’s eat() method actually declares that it throws a more specific exception: MeatInedibleException. It can do this because MeatInedibleException is a subtype of InedibleException. If we are working with an Herbivore type directly, the compiler will allow us to catch just the MeatInedibleException and not require us to guard against the more general InedibleException:

    Herbivore creature = ...
    try {
        creature.eat( food );
    } catch ( MeatInedibleException ) {
        // creature can't eat this food because it's meat
    }

On the other hand, if we don’t care why the food is inedible, we’re free to guard for the more general InedibleException alone and treat it as any other Animal.

To sum up, an overriding method can refine not only the behavior of the parent method, but also the type of checked exceptions it throws. Next, we’ll talk about overridden methods that change their return type in exactly the same way.

Return types and overridden methods

For a method to qualify as an overridden method in a subclass, it must have exactly the same number and types of arguments. It must have the same “inputs,” as it were. As we saw in the previous section, overriding methods may refine their “output” to some extent. Namely, they can narrow their throws clause by declaring that they throw subtypes of the original method’s exception types. What about the main “output” of a method? Its return value? Can we change the return type of a method by overriding it? The answer is that Java gives us covariant return types on methods just as it does for exception types.

What this means is that when you override a method, you may change the return type to a subtype of the original method’s return type. For example, if our Animal class has a factory method called create() that produces an instance of Animal, our Mammal class could refine the return type to Mammal:

    class Animal {
        Animal create() { ... }
    }
    class Mammal extends Animal {
        Mammal create() { ... }
    }

As we’ll see later, this coding technique is very helpful because it eliminates some runtime casting of objects.

Special References: this and super

The special references this and super allow you to refer to the members of the current object instance or to members of the superclass, respectively. We have seen this used elsewhere to pass a reference to the current object and to refer to shadowed instance variables. The reference super does the same for the parents of a class. You can use it to refer to members of a superclass that have been shadowed or overridden. Being able to invoke the original method of the superclass allows us to use it as part of our new method, delegating to its behavior before or after we perform additional work:

    class Animal {
        void eat( Food f ) throws InedibleException {
            // consume food
        }
    }

    class Herbivore extends Animal {
        void eat( Food f ) throws MeatInedibleException {
            // check if edible
            ...
            try {
                super.eat( f );
            } catch ( InedibleException e ) { ... }
        }
    }

In this example, our Herbivore class overrides the Animal eat() method to first do some checking on the food object. After doing its job, it uses super.eat() to call the (otherwise overridden and inaccessible) implementation of eat() in its superclass.

super prompts a search for the method or variable to begin in the scope of the immediate superclass rather than the current class. The inherited method or variable found may reside in the immediate superclass or one further up the tree. The usage of the super reference when applied to overridden methods of a superclass is special; it tells the method resolution system to stop the dynamic method search at the superclass instead of at the most derived class (as it otherwise does). Without super, there would be no way to access overridden methods.

Casting

A cast explicitly tells the compiler to change the apparent type of an object reference. The main use for casts is when an object is temporarily assigned to a more general type. For example, if a String were assigned to a variable of type Object, to use it as a String again, we’d have to perform a cast to get it back. The compiler recognizes only the declared types of variables and doesn’t know that we actually placed a String into it. In Java, casts are checked both at compile time and at runtime to make sure they are legal. At compile time the Java compiler will stop you from trying to perform a cast that cannot possibly work (such as turning a Date directly into a String). And at runtime, Java will check that casts that are plausible (such as our Object to String) are actually correct for the real objects involved.

Attempting to cast an object to an incompatible type at runtime results in a ClassCastException. Only casts between objects in the same inheritance hierarchy (and, as we’ll see later, to appropriate interfaces) are legal in Java and pass the scrutiny of the compiler and the runtime system. Casts in Java affect only the treatment of references; they never change the form of the actual object. This is an important rule to keep in mind. You never change the object pointed to by a reference by casting it; you change only the compiler’s (or runtime system’s) notion of it.

A cast can be used to narrow or downcast the type of a reference—to make it more specific. Often, we’ll do this when we have to retrieve an object from a more general type of collection or when it has been previously used as a less derived type. (The prototypical example is using an object in a collection, as we’ll see in Chapter 11.) Continuing with our Cat example:

    Animal creature;
    Cat simon;
    // ...

    creature = simon;        // OK
    // simon = creature;     // Compile-time error, incompatible type
    simon = (Cat)creature;   // OK

We can’t reassign the reference in creature to the variable simon even though we know it holds an instance of a Cat (Simon). We have to perform the indicated cast to narrow the reference. Note that an implicit cast was performed when we went the other way to widen the reference simon to type Animal during the first assignment. In this case, an explicit cast would have been legal but superfluous.

What all this means is that you can’t lie or guess about what an object is. If you have a Cat object, you can use it as an Animal or even Object because all Java classes are a subclass of Object. But if you have an Object you think is a Cat, you have to perform a cast to get it back to an Animal or a Cat. If you aren’t sure whether the Object is a Cat or a Dog at runtime, you can check it with instanceof before you perform the cast. If you do not check and you get the cast wrong, the runtime system throws a ClassCastException.

    if ( creature instanceof Cat ) {
        Cat cat = (Cat)creature;
        cat.meow();
    }

As we mentioned earlier, casting can affect the selection of compile-time items such as variables and overloaded methods, but not the selection of overridden methods. Figure 6-4 shows the difference. As shown in the top half of the diagram, casting the reference simon to type Animal (widening it) affects the selection of the shadowed variable weight within it. However, as the lower half of the diagram indicates, the cast doesn’t affect the selection of the overridden method sleep().

Casting and selection of methods and variables
Figure 6-4. Casting and selection of methods and variables

Casting aspersions

Casting in Java is something that programmers strive to avoid. This is not only because it indicates a weakness in the static typing of the code, but because casts can also simply be tedious to use and make code less readable. Unfortunately, a great deal of code written in Java in the past has had no choice but to rely on casting so that it can work with any type of object the user requires. Java 5.0 introduced a major new language feature, generics, partly to address this issue. Generics allow Java code to be “typed” for a particular kind of object by the user, eliminating the need to cast in many situations. We’ll cover generics in detail in Chapter 8 and see how they reduce the need for casts in most Java code.

Using Superclass Constructors

When we talked earlier about constructors, we discussed how the special statement this() invokes an overloaded constructor upon entry to another constructor. Similarly, the statement super() explicitly invokes the constructor of a superclass. Of course, we also talked about how Java makes a chain of constructor calls that includes the superclass’s constructor, so why use super() explicitly? When Java makes an implicit call to the superclass constructor, it calls the default constructor. If we want to invoke a superclass constructor that takes arguments, we have to do so explicitly using super().

If we are going to call a superclass constructor with super(), it must be the first statement of our constructor, just as this() must be the first call we make in an overloaded constructor. Here’s a simple example:

    class Person {
        Person ( String name ) {
            //  setup based on name
            ...
        }
        ...
    }

    class Doctor extends Person {
        Doctor ( String name, String specialty ) {
            super( name );
            // setup based on specialty
            ...
        }
        ...
    }

In this example, we use super() to take advantage of the implementation of the superclass constructor and avoid duplicating the code to set up the object based on its name. In fact, because the class Person doesn’t define a default (no arguments) constructor, we have no choice but to call super() explicitly. Otherwise, the compiler would complain that it couldn’t find an appropriate default constructor to call. In other words, if you subclass a class whose constructors all take arguments, you have to invoke one of the superclass’s constructors explicitly from at least one of your subclass’s constructors.

Instance variables of the class are initialized upon return from the superclass constructor, whether that’s due to an explicit call to super() or an implicit call to the default superclass constructor.

Full Disclosure: Constructors and Initialization

We can now tell the full story of how constructors are chained together and when instance variable initialization occurs. The rule has three parts and is applied repeatedly for each successive constructor that is invoked:

  • If the first statement of a constructor is an ordinary statement—that is, not a call to this() or super()—Java inserts an implicit call to super() to invoke the default constructor of the superclass. Upon returning from that call, Java initializes the instance variables of the current class and proceeds to execute the statements of the current constructor.

  • If the first statement of a constructor is a call to a superclass constructor via super(), Java invokes the selected superclass constructor. Upon its return, Java initializes the current class’s instance variables and proceeds with the statements of the current constructor.

  • If the first statement of a constructor is a call to an overloaded constructor via this(), Java invokes the selected constructor, and upon its return, simply proceeds with the statements of the current constructor. The call to the superclass’s constructor has happened within the overloaded constructor, either explicitly or implicitly, so the initialization of instance variables has already occurred.

Abstract Methods and Classes

A method in Java can be declared with the abstract modifier to indicate that it’s just a prototype. An abstract method has no body; it’s simply a signature declaration followed by a semicolon. You can’t directly use a class that contains an abstract method; you must instead create a subclass that implements the abstract method’s body:

    abstract void vaporMethod( String name );

In Java, a class that contains one or more abstract methods must be explicitly declared as an abstract class, also using the abstract modifier:

    abstract classVaporClass {
        ...
        abstract void vaporMethod( String name );
        ...
    }

An abstract class can contain other nonabstract methods and ordinary variable declarations, but it can’t be instantiated. To be used, it must be subclassed and its abstract methods must be "overridden" with methods that implement a body. Not all abstract methods have to be implemented in a single subclass, but a subclass that doesn’t override all its superclass’s abstract methods with actual, concrete implementations must also be declared abstract.

    class MyVaporImplementation extends VaporClass {
        void vaporMethod( String name ) { ... }    
    }

Abstract classes provide a framework for classes that is to be “filled in” by the implementer. The java.io.InputStream class, for example, has a single abstract method called read(). Various subclasses of InputStream implement read() in their own ways to read from their own sources. The rest of the InputStream class, however, provides extended functionality built on the simple read() method. A subclass of InputStream inherits these nonabstract methods to provide functionality based on the simple read() method that the subclass implements.

Interfaces

Java expands on the concept of abstract methods with interfaces. It’s often desirable to specify a group of abstract methods defining some behavior for an object without tying it to any implementation at all. In Java, this is called an interface. An interface defines a set of methods that a class must implement. A class in Java can declare that it implements an interface if it implements the required methods. Unlike extending an abstract class, a class implementing an interface doesn’t have to inherit from any particular part of the inheritance hierarchy or use a particular implementation.

Interfaces are kind of like Boy Scout or Girl Scout merit badges. A scout who has learned to build a birdhouse can walk around wearing a little sleeve patch with a picture of one. This says to the world, “I know how to build a birdhouse.” Similarly, an interface is a list of methods that define some set of behavior for an object. Any class that implements each method listed in the interface can declare at compile time that it implements the interface and wear, as its merit badge, an extra type—the interface’s type.

Interface types act like class types. You can declare variables to be of an interface type, you can declare arguments of methods to accept interface types, and you can specify that the return type of a method is an interface type. In each case, what is meant is that any object that implements the interface (i.e., wears the right merit badge) can fill that role. In this sense, interfaces are orthogonal to the class hierarchy. They cut across the boundaries of what kind of object an item is and deal with it only in terms of what it can do. A class can implement as many interfaces as it desires. In this way, interfaces in Java replace much of the need for multiple inheritance in other languages (and all its messy complications).

An interface looks, essentially, like a purely abstract class (i.e., a class with only abstract methods). You define an interface with the interface keyword and list its methods with no bodies, just prototypes (signatures):

    interface Driveable {
        boolean startEngine();
        void stopEngine();
        float accelerate( float acc );
        boolean turn( Direction dir );
    }

The previous example defines an interface called Driveable with four methods. It’s acceptable, but not necessary, to declare the methods in an interface with the abstract modifier; we haven’t done that here. More importantly, the methods of an interface are always considered public, and you can optionally declare them as so. Why public? Well, the user of the interface wouldn’t necessarily be able to see them otherwise, and interfaces are generally intended to describe the behavior of an object, not its implementation.

Interfaces define capabilities, so it’s common to name interfaces after their capabilities. Driveable, Runnable, and Updateable are good interface names. Any class that implements all the methods can then declare that it implements the interface by using a special implements clause in its class definition. For example:

    class Automobile implements Driveable {
        ...
        public boolean startEngine() {
            if ( notTooCold )
                engineRunning = true;
            ...
        }

        public void stopEngine() {
            engineRunning = false;
        }

        public float accelerate( float acc ) {
            ...
        }

        public boolean turn( Direction dir ) {
            ...
        }
        ...
    }

Here, the class Automobile implements the methods of the Driveable interface and declares itself a type of Driveable using the implements keyword.

As shown in Figure 6-5, another class, such as Lawnmower, can also implement the Driveable interface. The figure illustrates the Driveable interface being implemented by two different classes. While it’s possible that both Automobile and Lawnmower could derive from some primitive kind of vehicle, they don’t have to in this scenario.

After declaring the interface, we have a new type, Driveable. We can declare variables of type Driveable and assign them any instance of a Driveable object:

    Automobile auto = new Automobile();
    Lawnmower mower = new Lawnmower();
    Driveable vehicle;

    vehicle = auto;
    vehicle.startEngine();
    vehicle.stopEngine();

    vehicle = mower;
    vehicle.startEngine();
    vehicle.stopEngine();
Implementing the Driveable interface
Figure 6-5. Implementing the Driveable interface

Both Automobile and Lawnmower implement Driveable, so they can be considered interchangeable objects of that type.

Interfaces as Callbacks

Interfaces can be used to implement “callbacks” in Java. This is when an object effectively passes a reference to one or more of its methods to another object. The callback occurs when the called object subsequently invokes one of the methods. In C or C++, this is prime territory for function pointers; Java uses interfaces instead. More generally, this concept is extended in Java to the concept of events in which listener objects register with event sources. We’ll cover events in great detail in later chapters.

Consider two classes: a TickerTape class that displays data and a TextSource class that provides an information feed. We’d like our TextSource to send any new text data. We could have TextSource store a reference to a TickerTape object, but then we could never use our TextSource to send data to any other kind of object. Instead, we’d have to proliferate subclasses of TextSource that dealt with different types. A more elegant solution is to have TextSource store a reference to an interface type, TextReceiver:

    interface TextReceiver {
        void receiveText( String text );
    }

    class TickerTape implements TextReceiver {
        public void receiveText( String text ) {
            System.out.println("TICKER:\n" + text + "\n");
        }
    }

    class TextSource {
        TextReceiver receiver;

        TextSource( TextReceiver r ) {
            receiver = r;
        }

        public void sendText( String s ) {
            receiver.receiveText( s );
        }
    }

The only thing TextSource really cares about is finding the right method to invoke in order to output some text. Using an interface establishes a “contract,” receiveText(), for that method.

When the TextSource is constructed, a reference to the TickerTape (which implements the interface) is stored in an instance variable. This “registers” the TickerTape as the TextSource’s “output device.” Whenever it needs to output data, the TextSource calls the output device’s receiveText() method. Later, we’ll see that many APIs in Java use a model like this, but more often many “receivers” may register with the same source.

Interface Variables

Although interfaces mostly allow us to specify behavior without implementation, there’s one exception. An interface can contain constants (static final variables ), which can be referred to directly through the interface name, and which also appear in any class that implements the interface. This feature allows constants to be packaged for use with the methods of the interface:

    interface Scaleable {
        static final int BIG = 0, MEDIUM = 1, SMALL = 2;
        void setScale( int size );
    }

The Scaleable interface defines three integers: BIG, MEDIUM, and SMALL. All variables defined in interfaces are implicitly final and static; you don’t need to use the modifiers, but for clarity, we recommend that you do. A class that implements Scaleable sees these constants:

    class Box implements Scaleable {

        void setScale( int size ) {
            switch( size ) {
                case BIG:
                    ...
                case MEDIUM:
                    ...
                case SMALL:
                    ...
            }
        }
        ...
    }

While there is nothing technically wrong with using interfaces in this way, the main incentive for doing so disappeared when Java added enumerations and static imports. Using interfaces for this purpose is bad because all those public, static constants then appear in the public API of your class and can confuse those who use it. What’s worse, you can’t remove them later because other code may rely on the class that contains those values. It’s better to use an enumeration or to put your constants in their own class and then use the new static import syntax to remove the hassle of referring to them. We’ll discuss static import later in this chapter. This code snippet gives a glimpse of how it works:

    enum SizeConstants { BIG, MEDIUM, SMALL }

    // usage
    static import mypackage.SizeConstants;
    ...
    setSize( MEDIUM );

Flag interfaces

Sometimes completely empty interfaces serve as a marker that a class has a special property. The java.io.Serializeable interface is a good example. Classes that implement Serializeable don’t have to add any methods or variables. Their additional type simply identifies them to Java as classes that want to be able to be serialized. This usage of interfaces is less important now that Java has annotations, described in Chapter 7.

Subinterfaces

An interface can extend another interface, just as a class can extend another class. Such an interface is called a subinterface. For example:

    interface DynamicallyScaleable extends Scaleable {
        void changeScale( int size );
    }

The interface DynamicallyScaleable extends our previous Scaleable interface and adds an additional method. A class that implements DynamicallyScaleable must implement all the methods of both interfaces.

Note here that we are using the term extends and not implements to subtype the interface. Interfaces can’t implement anything! But an interface is allowed to extend as many interfaces as it wants. If you want to extend two or more interfaces, list them after the extends keyword, separated by commas:

    interface DynamicallyScaleable extends Scaleable, SomethingElseable {
        ...
    }

A class that implements this interface must also implement the other interfaces. Furthermore, interface subtypes are assignable to their supertypes in the same way that classes are, so an instance of DynamicallyScaleable can be assigned to a variable of type Scaleable, as you might expect.

Overlapping and conflicting methods

We should also note the possibility that when an interface extends two or more interfaces (or when a class implements two or more interfaces), there may be overlapping or conflicting methods in those interfaces. If two methods in different interfaces have exactly the same signature and return type, there is no problem and the implementation in the class satisfies both interfaces. If the methods differ in the way that overloaded methods do, the class must implement both method signatures. If the methods have the same name but differ in return or exception types, the class cannot implement both and compile-time errors occur.

Packages and Compilation Units

A package is a name for a group of related classes and interfaces. In Chapter 3, we discussed how Java uses package names to locate classes during compilation and at runtime. In this sense, packages are somewhat like libraries; they organize and manage sets of classes. Packages provide more than just source-code-level organization. They create an additional level of scope for their classes and the variables and methods within them. We’ll talk about the visibility of classes later in this section. In the next section, we discuss the effect that packages have on access to variables and methods among classes.

Compilation Units

The source code for Java classes is organized into compilation units. A simple compilation unit contains a single class definition and is named for that class. The definition of a class named MyClass, for instance, could appear in a file named MyClass.java. For most of us, a compilation unit is just a file with a .java extension, but theoretically in an IDE, it could be an arbitrary entity. For brevity, we’ll refer to a compilation unit simply as a file.

The division of classes into their own files is important because the Java compiler assumes much of the responsibility of a make or build utility. The compiler relies on the names of source files to find and compile dependent classes. It’s possible to put more than one class definition into a single file, but there are some restrictions that we’ll discuss shortly.

A class is declared to belong to a particular package with the package statement. The package statement must appear as the first statement in a file. There can be only one package statement, and it applies to the entire file:

    package mytools.text;

    class TextComponent {
         ...
    }

In this example, the class TextComponent is placed in the package mytools.text.

Package Names

Package names are hierarchical in nature, using a dot-separated naming convention. By default, package name components correspond to directory names and serve as a unique path for the compiler and runtime systems to locate Java source files and classes. However, other than for locating files, package names in Java do not create real relationships between packages. There is really no such thing as a “subpackage.” The package namespace is actually flat, not hierarchical. Packages under a particular part of a package hierarchy are related only by convention. For example, if we create another package called mytools.text.poetry (presumably for text classes that are specialized in some way to work with poetry), those classes won’t be part of the mytools.text package; they won’t have the access privileges of package members. In this sense, the package-naming convention can be misleading. One minor deviation from this notion is that assertions, which we described in Chapter 4, can be turned on or off for a package and all packages “under” it. But that is really just a convenience and not represented in the code structure.

Class Visibility

By default, a class is accessible only to other classes within its package. This means that our TextComponent class is available only to other classes in the mytools.text package. To be used outside of its package, a class must be declared as public:

    package mytools.text;

    public class TextEditor {
         ...
    }

The class TextEditor can now be referenced anywhere. A Java source code file can have only a single public class defined within it and the file must be named for that class.

By hiding unimportant or extraneous classes, a package builds a subsystem that has a well-defined interface to the rest of the world. Public classes provide a facade for the operation of the system. The details of its inner workings can remain hidden, as shown in Figure 6-6. In this sense, packages can hide classes in the way classes hide private members. Nonpublic classes within a package are sometimes called package private for this reason.

Packages and class visibility
Figure 6-6. Packages and class visibility

Figure 6-6 shows part of the hypothetical mytools.text package. The classes TextArea and TextEditor are declared public so that they can be used elsewhere in an application. The class TextComponent is part of the implementation of TextArea and is not accessible from outside of the package.

Importing Classes

Classes within a package can refer to each other by their simple names. However, to locate a class in another package, we have to be more specific. Continuing with the previous example, an application can refer directly to our editor class by its fully qualified name of mytools.text.TextEditor. But we’d quickly grow tired of typing such long class names, so Java gives us the import statement. One or more import statements can appear at the top of a compilation unit, after the package statement. The import statements list the fully qualified names of classes and packages to be used within the file.

Like a package statement, an import statement applies to the entire compilation unit. Here’s how you might use an import statement:

    package somewhere.else;
    import mytools.text.TextEditor;

    class MyClass {
        TextEditor editBoy;
        ...
    }

As shown in this example, once a class is imported, it can be referenced by its simple name throughout the code. It is also possible to import all the classes in a package using the * wildcard notation:

    import mytools.text.*;

Now we can refer to all public classes in the mytools.text package by their simple names.

Obviously, there can be a problem with importing classes that have conflicting names. The compiler prevents you from explicitly importing two classes with the same name and gives you an error if you try to use an ambiguous class that could come from two packages imported with the package import notation. In this case, you just have to fall back to using fully qualified names to refer to those classes. You can either use the fully qualified name directly, or you can add an additional, single class import statement that disambiguates the class name. It doesn’t matter whether this comes before or after the package import.

Other than the potential for naming conflicts, there’s no penalty for importing many classes. Java doesn’t carry extra baggage into the compiled class files. In other words, Java class files don’t contain information about the imports; they only reference classes actually used in them.

One note about conventions: in an effort to keep our examples short, we’ll sometimes import entire packages (.*) even when we use only a class or two from it. In practice, it’s usually better to be specific when possible and list individual, fully qualified class imports if there are only a few of them. Some people (especially those using IDEs that do it for them) avoid using package imports entirely, choosing to list every imported class individually. Usually, a compromise is your best bet. If you are going to use more than two or three classes from a package, consider the package import.

The unnamed package

A class that is defined in a compilation unit that doesn’t specify a package falls into the large, amorphous unnamed package. Classes in this nameless package can refer to each other by their simple names. Their path at compile time and runtime is considered to be the current directory, so packageless classes are useful for experimentation and testing (and for brevity in examples in books about Java).

Static imports

A static import is a variation of the import statement that allows you to import static members of a class into the namespace of your file so that you don’t have to qualify them when you use them. The best example of this is in working with the java.lang.Math class. With static import, we can get an illusion of built-in math “functions” and constants like so:

    import static java.lang.Math.*;

    // usage
    double circumference = 2 * PI * radius;
    double length = sin( theta ) * side;
    int bigger = max( a, b );
    int positive = abs( num );

This example imports all of the static members of the java.lang.Math class. We can also import individual members by name:

    import static java.awt.Color.RED;
    import static java.awt.Color.WHITE;
    import static java.awt.Color.BLUE;

    // usage
    setField( BLUE );
    setStripe( RED );
    setStripe( WHITE );

To be precise, these static imports are importing a name, not a specific member, into the namespace of our file. For example, importing the name “foo” would bring in any constants named foo as well as any methods named foo() in the class.

Static imports are compelling and make code more succinct. Their usage, however, goes somewhat against the concepts of object-oriented programming. Static imports are best for utilities and other global convenience methods that do not require much context.

Visibility of Variables and Methods

One of the most important aspects of object-oriented design is data hiding, or encapsulation. By treating an object in some respects as a “black box” and ignoring the details of its implementation, we can write more resilient, simpler code with components that can be easily reused.

Basic Access Modifiers

By default, the variables and methods of a class are accessible to members of the class itself and to other classes in the same package. To borrow from C++ terminology, classes in the same package are friendly. We’ll call this the default level of visibility. As you’ll see as we go on, the default visibility lies in the middle of the range of restrictiveness that can be specified.

The modifiers public and private, on the other hand, define the extremes. As we mentioned earlier, methods and variables declared as private are accessible only within their class. At the other end of the spectrum, members declared as public are accessible from any class in any package, provided the class itself can be seen. (The class that contains the methods must also be public to be seen outside of its package, as we discussed previously.) The public members of a class should define its most general functionality—what the black box is supposed to do.

Figure 6-7 illustrates the four simplest levels of visibility, continuing the example from the previous section. Public members in TextArea are accessible from anywhere. Private members are not visible from outside the class. The default visibility allows access by other classes in the package.

Private, default, protected, and public visibility
Figure 6-7. Private, default, protected, and public visibility

The protected modifier allows special access permissions for subclasses. Contrary to how it might sound, protected is slightly less restrictive than the default level of accessibility. In addition to the default access afforded classes in the same package, protected members are visible to subclasses of the class, even if they are defined in a different package. If you are a C++ programmer used to more restrictive meanings, this may rub you the wrong way.[18]

Table 6-1 summarizes the levels of visibility available in Java; it runs generally from most to least restrictive. Methods and variables are always visible within a declaring class itself, so the table doesn’t address that scope.

Table 6-1. Visibility modifiers

Modifier

Visibility outside the class

private

None

No modifier (default)

Classes in the package

protected

Classes in package and subclasses inside or outside the package

public

All classes

Subclasses and Visibility

Subclasses add two important (but unrelated) complications to the topic of visibility. First, when you override methods in a subclass, the overriding method must be at least as visible as the overridden method. While it is possible to take a private method and override it with a public method in a subclass, the reverse is not possible; you can’t override a public method with a private method. This restriction makes sense if you recall that subtypes have to be usable as instances of their supertype (e.g., a Mammal is a subclass of Animal and, therefore, must be usable as an Animal). If we could override a method with a less visible method, we would have a problem: our Mammal might not be able to do all the things an Animal can. However, we can reduce the visibility of a variable. In this case, the variable acts like any other shadowed variable; the two variables are distinct and can have separate visibilities in different classes.

The next complication is a bit harder to follow: the protected variables of a class are visible to its subclasses, but only through objects of the subclass’s type or its subtypes. In other words, a subclass can see a protected variable of its superclass as an inherited variable, but it can’t access that same variable via a reference to the superclass itself. This statement could be confusing because it might not be obvious that visibility modifiers don’t restrict access between instances of the same class in the same way that they restrict access between instances of different classes. Two instances of the same class can access all of each other’s members, including private ones, as long as they refer to each other as the correct type. Said another way: two instances of Cat can access all of each other’s variables and methods (including private ones), but a Cat can’t access a protected member in an instance of Animal unless the compiler can prove that the Animal is a Cat. That is, Cats have the special privileges of being an Animal only with respect to other Cats, not just any Animal. If you find this hard to follow, don’t worry too much. If you run into this as a problem in the real world, you are probably trying to do something trickier than you should.

Interfaces and Visibility

Interfaces behave like classes within packages. An interface can be declared public to make it visible outside its package. Under the default visibility, an interface is visible only inside its package. Like classes, only one public interface can be declared in a compilation unit (file).

Arrays and the Class Hierarchy

Now we’re going to shift gears a bit and return to the topic of arrays, considering them from the object point of view. At the end of Chapter 4, we mentioned that arrays have a place in the Java class hierarchy, but we didn’t give you any details. Now that we’ve discussed the object-oriented aspects of Java, we can give you the whole story.

Array classes live in a parallel Java class hierarchy under the Object class. If a class is a direct subclass of Object, an array class for that base type also exists as a direct subclass of Object. Arrays of more derived classes are subclasses of the corresponding array classes. For example, consider the following class types:

    class Animal { ... }
    class Bird extends Animal { ... }
    class Penguin extends Bird { ... }

Figure 6-8 illustrates the class hierarchy for arrays of these classes. Arrays of the same dimension are related to one another in the same manner as their base type classes. In our example, Bird is a subclass of Animal, which means that the Bird[] type is a subtype of Animal[]. In the same way a Bird object can be used in place of an Animal object, a Bird[] array can be assigned to a variable of type Animal[]:

    Animal [][] animals;
    Bird [][] birds = new Bird [10][10];
    birds[0][0] = new Bird();

    // make animals and birds reference the same array object
    animals = birds;
    observe( animals[0][0] );               // processes Bird object

Because arrays are part of the class hierarchy, we can use instanceof to check the type of an array:

    if ( birds instanceof Animal[][] )      // true

An array is a type of Object and thus can be assigned to Object type variables:

    Object obj = animals;

Because Java knows the actual type of all objects, you can also cast back if appropriate:

    animals = (Animal [][])something;
Arrays in the Java class hierarchy
Figure 6-8. Arrays in the Java class hierarchy

ArrayStoreException

Because arrays have the property that an array of one type is assignable to an array of its supertype, it is possible to play games with the compiler and try to trick it into storing the wrong kind of object in an array. Java may not be able to check the types of all objects that you place into arrays at compile time. In those cases, it’s possible to receive an ArrayStoreException at runtime if you try to assign the wrong type of object to an array element. For example:

    String [] strings = new String [10];
    Object [] objects = strings;  // alias String [] as Object []
    objects[0] = new Date(); // Runtime ArrayStoreException!

Here, we have “aliased” a String [] by assigning it to an Object []. By the third line, the compiler no longer knows the actual type of array stored in the object’s variable and has no choice but to let us try whatever we want. Of course, at runtime the VM realizes that we are trying to put a Date object into an array of Strings and throws the ArrayStoreException for us. This type of problem shouldn’t happen often for you in straightforward array use. We mention it here because the concept will come up again when we talk about generics in Chapter 8.

Inner Classes

All of the classes we’ve seen so far in this book have been top-level, “freestanding” classes declared at the file and package level. But classes in Java can actually be declared at any level of scope, within any set of curly braces (i.e., almost anywhere that you could put any other Java statement). These inner classes belong to another class or method as a variable would and may have their visibility limited to its scope in the same way. Inner classes are a useful and aesthetically pleasing facility for structuring code. Their cousins, anonymous inner classes, are an even more powerful shorthand that make it seem as if you can create new kinds of objects dynamically within Java’s statically typed environment. In Java, anonymous inner classes play part of the role of closures in other languages, giving the effect of handling state and behavior independently of classes.

However, as we delve into their inner workings, we’ll see that inner classes are not quite as aesthetically pleasing or dynamic as they seem. Inner classes are pure syntactic sugar; they are not supported by the VM and are instead mapped to regular Java classes by the compiler. As a programmer, you may never need be aware of this; you can simply rely on inner classes like any other language construct. However, you should know a little about how inner classes work to better understand the compiled code and a few potential side effects.

Inner classes are essentially nested classes, for example:

    Class Animal {
        Class Brain {
            ...
        }
    }

Here, the class Brain is an inner class: it is a class declared inside the scope of class Animal. Although the details of what that means require a bit of explanation, we’ll start by saying that Java tries to make the meaning, as much as possible, the same as for the other members (methods and variables) living at that level of scope. For example, let’s add a method to the Animal class:

    Class Animal {
        Class Brain {
            ...
        }
        void performBehavior() { ... }
    }

Both the inner class Brain and the method performBehavior() are within the scope of Animal. Therefore, anywhere within Animal, we can refer to Brain and performBehavior() directly, by name. Within Animal, we can call the constructor for Brain (new Brain()) to get a Brain object or invoke performBehavior() to carry out that method’s function. But neither Brain nor performBehavior() are generally accessible outside of the class Animal without some additional qualification.

Within the body of the inner Brain class and the body of the performBehavior() method, we have direct access to all the other methods and variables of the Animal class. So, just as the performBehavior() method could work with the Brain class and create instances of Brain, methods within the Brain class can invoke the performBehavior() method of Animal as well as work with any other methods and variables declared in Animal. The Brain class “sees” all of the methods and variables of the Animal class directly in its scope.

That last bit has important consequences. From within Brain, we can invoke the method performBehavior(); that is, from within an instance of Brain, we can invoke the performBehavior() method of an instance of Animal. Well, which instance of Animal? If we have several Animal objects around (say, a few Cats and Dogs), we need to know whose performBehavior() method we are calling. What does it mean for a class definition to be “inside” another class definition? The answer is that a Brain object always lives within a single instance of Animal: the one that it was told about when it was created. We’ll call the object that contains any instance of Brain its enclosing instance.

A Brain object cannot live outside of an enclosing instance of an Animal object. Anywhere you see an instance of Brain, it will be tethered to an instance of Animal. Although it is possible to construct a Brain object from elsewhere (i.e., another class), Brain always requires an enclosing instance of Animal to “hold” it. We’ll also say now that if Brain is to be referred to from outside of Animal, it acts something like an Animal.Brain class. And just as with the performBehavior() method, modifiers can be applied to restrict its visibility. All of the usual visibility modifiers apply, and inner classes can also be declared static, as we’ll discuss later.

We’ve said that within the Animal class, we can construct a Brain in the ordinary way, using new Brain(), for example. Although we’d probably never find a need to do it, we can also construct an instance of Brain from outside the class by referencing an instance of Animal. To do this requires that the inner class Brain be accessible and that we use a special form of the new operator designed just for inner classes:

    Animal monkey = new Animal();
    Animal.Brain monkeyBrain = monkey.new Brain();

Here, the Animal instance monkey is used to qualify the new operator on Brain. Again, this is not a very common thing to do and you can probably just forget that we said anything about it. Static inner classes are more useful. We’ll talk about them a bit later.

Inner Classes as Adapters

A particularly important use of inner classes is to make adapter classes. An adapter class is a “helper” class that ties one class to another in a very specific way. Using adapter classes, you can write your classes more naturally, without having to anticipate every conceivable user’s needs in advance. Instead, you provide adapter classes that marry your class to a particular interface. As an example, let’s say that we have an EmployeeList object:

    public class EmployeeList {
        private Employee [] employees = ... ;
        ...
    }

EmployeeList holds information about a set of employees. Let’s say that we would like to have EmployeeList provide its elements via an iterator. An iterator is a simple, standard interface to a sequence of objects. The java.util.Iterator interface has several methods:

    public interface Iterator {
        boolean hasNext();
        Object next();
        void remove();
    }

It lets us step through its elements, asking for the next one and testing to see if more remain. The iterator is a good candidate for an adapter class because it is an interface that our EmployeeList can’t readily implement itself. Why can’t the list implement the iterator directly? Because an iterator is a “one-way,” disposable view of our data. It isn’t intended to be reset and used again. It may also be necessary for there to be multiple iterators walking through the list at different points. We must, therefore, keep the iterator implementation separate from the EmployeeList itself. This is crying out for a simple class to provide the iterator capability. But what should that class look like?

Before we knew about inner classes, our only recourse would have been to make a new “top-level” class. We would probably feel obliged to call it EmployeeListIterator:

    class EmployeeListIterator implements Iterator {
        // lots of knowledge about EmployeeList
        ...
    }

Here we have a comment representing the machinery that the EmployeeListIterator requires. Think for just a second about what you’d have to do to implement that machinery. The resulting class would be completely coupled to the EmployeeList and unusable in other situations. Worse, in order to to function, it must have access to the inner workings of EmployeeList. We would have to allow EmployeeListIterator access to the private array in EmployeeList, exposing this data more widely than it should be. This is less than ideal.

This sounds like a job for inner classes. We already said that EmployeeListIterator was useless without an EmployeeList; this sounds a lot like the “lives inside” relationship we described earlier. Furthermore, an inner class lets us avoid the encapsulation problem because it can access all the members of its enclosing instance. Therefore, if we use an inner class to implement the iterator, the array employees can remain private, invisible outside the EmployeeList. So let’s just shove that helper class inside the scope of our EmployeeList:

    public class EmployeeList {
        private Employee [] employees = ... ;
        ...

        class Iterator implements java.util.Iterator {
            int element = 0;

            boolean hasNext() {
                return  element < employees.length ;
            }

            Object next() {
                if ( hasNext() )
                    return employees[ element++ ];
                else
                    throw new NoSuchElementException();
            }

            void remove() {
                 throw new UnsupportedOperationException();
            }
        }
    }

Now EmployeeList can provide a method like the following to let other classes work with the list:

    Iterator getIterator() {
            return new Iterator();
        }

One effect of the move is that we are free to be a little more familiar in the naming of our iterator class. Since it is no longer a top-level class, we can give it a name that is appropriate only within the EmployeeList. In this case, we’ve named it Iterator to emphasize what it does, but we don’t need a name like EmployeeIterator that shows the relationship to the EmployeeList class because that’s implicit. We’ve also filled in the guts of the Iterator class. As you can see, now that it is inside the scope of EmployeeList, Iterator has direct access to its private members, so it can directly access the employees array. This greatly simplifies the code and maintains compile-time safety.

Before we move on, we should note that inner classes can have constructors, variables, and initializers, even though we didn’t need one in this example. They are, in all respects, real classes.

Inner Classes Within Methods

Inner classes may also be declared for “local” use within the body of a method. Returning to the Animal class, we can put Brain inside the performBehavior() method if we decide that the class is useful only inside that method:

    Class Animal {
        void performBehavior() {
            Class Brain {
                ...
            }
        }
    }

In this situation, the rules governing what Brain can see are the same as in our earlier example. The body of Brain can see anything in the scope of performBehavior() and above it (in the body of Animal). This includes local variables of performBehavior() and its arguments. But because of the fleeting nature of a method invocation, there are a few limitations and additional restrictions, as described in the following sections. If you are thinking that inner classes within methods sounds arcane, bear with us until we talk about anonymous inner classes, which are tremendously useful.

Limitations on inner classes in methods

performBehavior() is a method, and method invocations have limited lifetimes. When they exit, their local variables normally disappear into the abyss. However, an instance of Brain (like any object created in the method) lives on as long as it is referenced. Java must make sure that any local variables used by instances of Brain created within an invocation of performBehavior() also live on. Furthermore, all the instances of Brain that we make within a single invocation of performBehavior() must see the same local variables. To accomplish this, the compiler must be allowed to make copies of local variables. Thus, their values cannot change once an inner class has seen them. This means that any of the method’s local variables or arguments that are referenced by the inner class must be declared final. The final modifier means that they are constant once assigned. This is a little confusing and easy to forget, but the compiler will graciously remind you. For example:

    void performBehavior( final boolean nocturnal )
    {
        class Brain {
            void sleep() {
                if ( nocturnal ) { ... }
            }
        }
    }

In this code snippet, the argument nocturnal to the performBehavior() method must be marked final so that it can be referenced within the inner class Brain. This is just a technical limitation of how inner classes are implemented, ensuring that it’s OK for the Brain class to keep a copy of the value.

Static inner classes

We mentioned earlier that the inner class Brain of the class Animal can, in some ways, be considered an Animal.Brain class—that is, it is possible to work with a Brain from outside the Animal class, using just such a qualified name: Animal.Brain. But as we described, given that our Animal.Brain class always requires an instance of an Animal as its enclosing instance, it’s not as common to work with them directly in this way.

However, there is another situation in which we want to use inner classes by name. An inner class that lives within the body of a top-level class (not within a method or another inner class) can be declared static. For example:

    class Animal  {
        static class MigrationPattern {
            ...
        }
        ...
    }

A static inner class such as this acts just like a new top-level class called Animal.MigrationPattern. We can use it just like any other class, without regard to any enclosing instances. Although this may seem strange, it is not inconsistent because a static member never has an object instance associated with it. The requirement that the inner class be defined directly inside a top-level class ensures that an enclosing instance won’t be needed. If we have permission, we can create an instance of the class using the qualified name:

    Animal.MigrationPattern stlToSanFrancisco =
        new Animal.MigrationPattern();

As you see, the effect is that Animal acts something like a minipackage, holding the MigrationPattern class. Here, we have used the fully qualified name, but we could also import it like any other class:

    import Animal.MigrationPattern;

This statement enables us to refer to the class simply as MigrationPattern. We can use all the standard visibility modifiers on inner classes, so a static inner class can have private, protected, default, or public visibility.

Here’s another example. The Java 2D API uses static inner classes to implement specialized shape classes (i.e., the java.awt.geom.Rectangle2D class has two inner classes, Float and Double, that implement two different precisions). These shape classes are actually very simple subclasses; it would have been sad to have to multiply the number of top-level classes in that package by three to accommodate all of them. With inner classes, we can bundle them with their respective classes:

    Rectangle2D.Float rect = new Rectangle2D.Float();

Anonymous inner classes

Now we get to the best part. As a general rule, the more deeply encapsulated and limited in scope our classes are, the more freedom we have in naming them. We saw this in our earlier iterator example. This is not just a purely aesthetic issue. Naming is an important part of writing readable, maintainable code. We generally want to use the most concise, meaningful names possible. A corollary to this is that we prefer to avoid doling out names for purely ephemeral objects that are going to be used only once.

Anonymous inner classes are an extension of the syntax of the new operation. When you create an anonymous inner class, you combine a class declaration with the allocation of an instance of that class, effectively creating a “one-time only” class and a class instance in one operation. After the new keyword, you specify either the name of a class or an interface, followed by a class body. The class body becomes an inner class, which either extends the specified class or, in the case of an interface, is expected to implement the interface. A single instance of the class is created and returned as the value.

For example, we could do away with the declaration of the Iterator class in the EmployeeList example by using an anonymous inner class in the getIterator() method:

    Iterator getIterator()
    {
        return new Iterator() {
            int element = 0;
            boolean hasNext() {
                return  element < employees.length ;
            }
            Object next() {
                if ( hasNext() )
                    return employees[ element++ ];
                else
                    throw new NoSuchElementException();
            }
            void remove() {
                 throw new UnsupportedOperationException();
            }
        };
    }

Here, we have simply moved the guts of Iterator into the body of an anonymous inner class. The call to new implicitly creates a class that implements the Iterator interface and returns an instance of the class as its result. Note the extent of the curly braces and the semicolon at the end. The getIterator() method contains a single statement, the return statement.

The previous example is a bit extreme and certainly does not improve readability. Inner classes are best used when you want to implement a few lines of code, but the verbiage and conspicuousness of declaring a separate class detracts from the task at hand. Here’s a better example. Suppose that we want to start a new thread to execute the performBehavior() method of our Animal:

    new Thread() {
        public void run() {  performBehavior();  }
    }.start();

Here, we have gone over to the terse side. We’ve allocated and started a new Thread, using an anonymous inner class that extends the Thread class and invokes our performBehavior() method in its run() method. The effect is similar to using a method pointer in some other language. However, the inner class allows the compiler to check type consistency, which would be more difficult (or impossible) with a true method pointer. At the same time, our anonymous adapter class with its three lines of code is much more efficient and readable than creating a new, top-level adapter class named AnimalBehaviorThreadAdapter.

While we’re getting a bit ahead of the story, anonymous adapter classes are a perfect fit for event handling (which we cover fully in Chapter 16). Skipping a lot of explanation, let’s say you want the method handleClicks() to be called whenever the user clicks the mouse. You would write code such as:

    addMouseListener( new MouseInputAdapter() {
        public void mouseClicked(MouseEvent e) { handleClicks(e); }
    } );

In this case, the anonymous class extends the MouseInputAdapter class by overriding its mouseClicked() method to call our method. A lot is going on in a very small space, but the result is clean, readable code. You assign method names that are meaningful to you while allowing Java to do its job of type checking.

Scoping of the “this” reference

Sometimes an inner class may want to get a handle on its “parent” enclosing instance. It might want to pass a reference to its parent or to refer to one of the parent’s variables or methods that has been hidden by one of its own. For example:

    class Animal {
        int size;
        class Brain {
            int size;
        }
    }

Here, as far as Brain is concerned, the variable size in Animal is shadowed by its own version.

Normally, an object refers to itself using the special this reference (implicitly or explicitly). But what is the meaning of this for an object with one or more enclosing instances? The answer is that an inner class has multiple this references. You can specify which this you want by prefixing it with the name of the class. For instance (no pun intended), we can get a reference to our Animal from within Brain, like so:

    class Brain {
        Animal ourAnimal = Animal.this;
        ...
    }

Similarly, we could refer to the size variable in Animal:

    class Brain {
        int animalSize = Animal.this.size;
        ...
    }

How do inner classes really work?

Finally, let’s get our hands dirty and take a look at what’s really going on when we use an inner class. We’ve said that the compiler is doing all the things that we had hoped to forget about. Let’s see what’s actually happening. Try compiling this trivial example:

    class Animal {
        class Brain {
        }
    }

What you’ll find is that the compiler generates two .class files: Animal.class and Animal$Brain.class.

The second file is the class file for our inner class. Yes, as we feared, inner classes are really just compiler magic. The compiler has created the inner class for us as a normal, top-level class and named it by combining the class names with a dollar sign. The dollar sign is a valid character in class names, but is intended for use only by automated tools. (Please don’t start naming your classes with dollar signs.) Had our class been more deeply nested, the intervening inner class names would have been attached in the same way to generate a unique top-level name.

Now take a look at the class with the JDK’s javap utility. Starting in Java 5.0, you can refer to the inner class as Animal.Brain, but in earlier versions of Java, you may have to call the class by its real name, Animal$Brain:

    % javap 'Animal$Brain'
    class Animal$Brain extends java.lang.Object {
        Animal$Brain(Animal);
    }

On a Windows system, it’s not necessary to quote the argument, as we did on this Unix command line.

You’ll see that the compiler has given our inner class a constructor that takes a reference to an Animal as an argument. This is how the real inner class gets the reference to its enclosing instance.

The worst thing about these additional class files is that you need to know they are there. Utilities such as jar don’t automatically find them; when you’re invoking such a utility, you need to specify these files explicitly or use a wildcard to find them:

    % jar cvf animal.jar Animal*class

Security implications

Given what we just saw—that the inner class really does exist as an automatically generated top-level class—how does it get access to private variables? The answer, unfortunately, is that the compiler is forced to break the encapsulation of your object and insert accessor methods so that the inner class can reach them. The accessor methods are given package-level access, so your object is still safe within its package walls, but it is conceivable that this difference could be meaningful if people were allowed to create new classes within your package.

The visibility modifiers on inner classes also have some problems. Current implementations of the VM do not implement the notion of a private or protected class within a package, so giving your inner class anything other than public or default visibility is only a compile-time guarantee. It is difficult to conceive of how these security issues could be abused, but it is interesting to note that Java is straining a bit to stay within its original design.[19]



[15] Note that a better way to design our calculators would be to have an abstract Calculator class with two subclasses: IntegerCalculator and DecimalCalculator.

[16] The Platypus is a highly unusual egg-laying Mammal. We could override the reproduce() behavior again for it in its own subclass of Mammal.

[17] An overridden method in Java acts like a virtual method in C++.

[18] Early on, the Java language allowed for certain combinations of modifiers, one of which was private protected. The meaning of private protected was to limit visibility strictly to subclasses (and remove package access). This was later deemed confusing and overly complex. It is no longer supported.

[19] Inner classes were added to Java in version 1.1.