close this bookExploring Java:By Patrick Niemeyer & Joshua Peck
source ref: ebookjava.html
View the documentMetadata
View the documentPreface
View the documentChapter 1: Yet Another Language?
View the documentChapter 2: A First Applet
View the documentChapter 3: Tools of the Trade
View the documentChapter 4: The Java Language
View the documentChapter 5: Objects in Java
View the documentChapter 4: The Java Language
View the documentChapter 6: Threads
View the documentChapter 7:Basic Utility Classes
View the documentChapter 8:Input/Output Facilities
View the documentChapter 9:Network Programming
View the documentChapter 10:Understand the Abstract Windowing Toolkit
View the documentChapter 11:Using and Creating GUI Components
View the documentChapter 12:Layout Managers
View the documentChapter 13:Drawing With the AWT
View the documentChapter 14:Working With Images

Chapter 4: The Java Language

Inside Arrays

At the end of Chapter 4, The Java Language, I mentioned that arrays have a place in the Java class hierarchy, but I didn't give you any details. Now that we've discussed the object-oriented aspects of Java, I 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, then 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 5.10 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 an Animal[] array:

Animal [][] animals; 
Bird [][] birds = new Bird [10][10]; 
birds[0][0] = new Bird(); 
 
// make animals and birds reference the same array object 
animals = birds;   
System.out.println( animals[0][0] );               // prints Bird 

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

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

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

Object something; 
something = animals; 

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

animals = (Animal [][])something; 

Under unusual circumstances, Java may not be able to check the types of objects you place into arrays at compile-time. In those cases, it's possible to receive an ArrayStoreException if you try to assign the wrong type of object to an array element. Consider the following:

class Dog { ... } 
class Poodle extends Dog { ... } 
class Chihuahua extends Dog { ... } 
 
Dog [] dogs; 
Poodle [] poodles = new Poodle [10]; 
 
dogs = poodles;  
 
dogs[3] = new Chihuahua();     // Run-time error, ArrayStoreException 

Both Poodle and Chihuahua are subclasses of Dog, so an array of Poodle objects can therefore be assigned to an array of Dog objects, as I described previously. The problem is that an object assignable to an element of an array of type Dog[] may not be assignable to an element of an array of type Poodle. A Chihuahua object, for instance, can be assigned to a Dog element because it's a subtype of Dog, but not to a Poodle element.[6]

[6] In some sense this could be considered a tiny hole in the Java type system. It doesn't occur elsewhere in Java, only with arrays. This is because array objects exhibit covariance in overriding their assignment and extraction methods. Covariance allows array subclasses to override methods with arguments or return values that are subtypes of the overridden methods, where the methods would normally be overloaded or prohibited. This allows array subclasses to operate on their base types with type safety, but also means that subclasses have different capabilities than their parents, leading to the problem shown above.


5.9 Inner Classes

We've left out something important in our discussion of Java classes so far: a large and relatively recent heap of syntactic sugar called inner classes. Simply put, classes in Java can be declared at any level of scope. That is, you can declare a class within any set of curly braces (that is, almost anywhere that you could put any other Java statement) and its visibility is limited to that scope in the same way that the name of a variable or method would be. Inner classes are a powerful and aesthetically pleasing facility for structuring code.[7] Their even sweeter cousins, anonymous inner classes, are another powerful shorthand that make it seem like you can create classes dynamically within Java's statically typed environment.

[7] The implementation of Java's inner classes draws on experience from the language Beta, and other block structured languages such as Pascal, and Scheme.

However, if you delve into the inner workings of Java, inner classes are not quite as aesthetically pleasing or dynamic. We said that they are syntactic sugar; by this we mean that they let you leverage the compiler by writing a few lines of code that trigger a lot of behind-the-scenes work somewhere between the compiler's front end and the byte-code. Inner classes rely on code-generation; they are a feature of the Java language, but not of the Java virtual machine. 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 results and a few potential side effects.

To this point, all of our classes have been top level classes. We have declared them, free standing, at the package level. Inner classes are essentially nested classes, like this:

    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 fair bit of explanation, we'll start by saying that the Java language tries to make the meaning, as much as possible, the same as for the other Java entities (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 accessible outside of the class Animal without some additional qualification.

Within the body of the Brain class and the body of the performBehavior() method, we have direct access to all of 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, code within the Brain class can invoke the performBehavior() method of Animal as well as work with any other methods and variables declared in Animal.

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. There is even an interpretation of the static modifier, which we'll talk about a bit later. However, the details are somewhat boring and not immediately useful, so you should consult a full language reference for more info (like O'Reilly's Java Language Reference, Second Edition). So before we get too far afield, let's turn to a more compelling example.

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, representing some view of our database. Let's say that we would like to have EmployeeList provide its elements as an enumeration (see Chapter 7, Basic Utility Classes). An enumeration is a simple interface to a set of objects that looks like this:

// the java.util.Enumeration interface
public interface Enumeration {
    boolean hasMoreElements();
    Object nextElement();
}

It lets us iterate through its elements, asking for the next one and testing to see if more remain. The enumeration is a good candidate for an adapter class because it is an interface that our EmployeeList can't readily implement itself. That's because an enumeration is a "one way", disposable view of our data. It isn't intended to be reset and used again, and therefore should be kept separate from the EmployeeList itself. This is crying out for a simple class to provide the enumeration capability. But what should that class look like?

Well, 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 EmployeeListEnumeration:

class EmployeeListEnumeration implements Enumeration {
    // lots of knowledge about EmployeeList
    ...
}

Here we have a comment representing the machinery that the EmployeeListEnumeration 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, to function it must have access to the inner workings of EmployeeList. We would have to allow EmployeeListEnumeration 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 EmployeeListEnumeration was useless without the 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 enumeration, 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 Enumerator implements java.util.Enumeration {
        int element = 0;
        boolean hasMoreElements() {
            return  element < employees.length ;
        }
        Object nextElement() {
            if ( hasMoreElements() )
                return employees[ element++ ];
            else 
                throw NoSuchElementException();
        }
    }
    
}

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

    ...
    Enumeration getEnumeration() {
        return new Enumerator();
    }

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

Before we move on, we should note that inner classes can have constructors, 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 withing the body of a method. Returning to the Animal class, we could put Brain inside the performBehavior() method if we decided that the class was only useful inside of 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, of course, above it. This includes local variables of performBehavior(), and its arguments. This raises a few questions.

performBehavior() is a method, and methods have limited lifetimes. When they exit their local variables normally disappear into the stacky abyss. But an instance of Brain (like any object) lives on as long as it is referenced. So Java makes sure that any local variables used by instances of Brain created within an invocation of performBehavior() also live on. Furthermore, all of the instances of Brain that we make within a single invocation of performBehavior() will see the same local variables.

Static Inner Classes

We mentioned earlier that the inner class Brain of the class Animal could 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 given that our Animal.Brain class always requires an instance of an Animal as its enclosing instance, some explicit setup is needed.[8]

[8] Specifically, we would have to follow a design pattern and pass a reference to the enclosing instance of Animal into the Animal.Brain constructor. See a language reference for more information. We don't expect you to run into this situation very often.

But there is another situation where we might 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 without regard to any enclosing instances. Although this seems strange, it is not inconsistent since 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 mini-package, holding the MigrationPattern class. We can use all of the standard visibility modifiers on inner classes, so a static inner class could be private, protected, default, or publicly visible.

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 enumeration example. This is not just a purely aesthetic issue. Naming is an important part of writing readable and maintainable code. We generally want to give things the most concise and meaningful names possible. A corollary to this is that we prefer to avoid doling out names for purely ephemeral objects that are only going to be used once.

Anonymous inner classes are an extension of the syntax of the new operation. When you create an anonymous inner class, you combine the class's declaration with the allocation of an instance of that class (much like the way you can declare a variable of the type of an un-named structure in C). After the new operator, 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 specified 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 Enumerator class in the EmployeeList example by using an anonymous inner class in the getEnumeration() method:

    ...
        
    Enumeration getEnumeration() {
    
        return new Enumeration() {
            int element = 0;
            boolean hasMoreElements() {
                return  element < employees.length ;
            }
            Object nextElement() {
                if ( hasMoreElements() )
                    return employees[ element++ ];
                else 
                    throw NoSuchElementException();
            }
        };
    }
    

Here we have simply moved the guts of Enumerator into the body of an anonymous inner class. The call to new implicitly constructs the class and returns an instance of the class as its result. Note the extent of the curly braces, and the semi-colon at the end. It is a single statement.

But the code above certainly does not improve readability. Inner classes are best used when you want to implement a few lines of code, where 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 ( new Runnable() {
        public void run() {  performBehavior();  }
    } ).start();

Here we have gone over to the terse side. We've allocated and started a new Thread, providing an anonymous inner class that implements the Runnable interface by calling our performBehavior() method. The effect is similar to using a method pointer in some other language; the inner class effectively substitutes the method we want called (performBehavior()) for the method the system wants to call (run()). However, the inner class allows the compiler to check type consistency, which would be difficult (if not 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'll cover fully in Chapter 10, Understand the Abstract Windowing Toolkit). 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 like this:

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

In this case, the anonymous class extends the AWT's MouseAdapter 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 get to assign method names that are meaningful to you, while allowing Java to do its job of type checking.

this and scoping

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 hidden 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 prepending the name of the class. So, 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, we'll 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 of the things that we had hoped to forget about. Let's see what's actually happening. Try compiling our simple example:

    class Animal {
        class Brain {
        }
    }

(Oh, come on, do it...)

What you'll find is that the compiler generates two .class files:

Animal.class
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 in this way. (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 it using the javap utility:

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

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 handle on its enclosing instance.

The worst thing about these additional class files is that you need to know they are there. Utilities like jar don't automatically find them; when you are invoking a utility like jar, you need to specify these files explicitly, or use a wild card that finds them.

Security Implications

Given what we just saw above--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 will be 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 virtual machine 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.


5.10 The Object and Class Classes

java.lang.Object is the mother of all objects; it's the primordial class from which all other classes are ultimately derived. Methods defined in Object are therefore very important because they appear in every instance of any class, throughout all of Java. At last count, there were nine public methods in Object. Five of these are versions of wait() and notify() that are used to synchronize threads on object instances, as we'll discuss in Chapter 6, Threads. The remaining four methods are used for basic comparison, conversion, and administration.

Every object has a toString() method that is called when it's to be represented as a text value. PrintStream objects use toString() to print data, as discussed in Chapter 8, Input/Output Facilities. toString() is also used when an object is referenced in a string concatenation. Here are some examples:

MyObj myObject = new MyObj(); 
Answer theAnswer = new Answer(); 
 
System.out.println( myObject ); 
String s = "The answer is: " + theAnswer ; 

To be friendly, a new kind of object should override toString() and implement its own version that provides appropriate printing functionality. Two other methods, equals() and hashCode(), may also require specialization when you create a new class.

Equality

equals() compares whether two objects are equivalent. Precisely what that means for a particular class is something that you'll have to decide for yourself. Two String objects, for example, are considered equivalent if they hold precisely the same characters in the same sequence:

String userName = "Joe"; 
... 
if ( userName.equals( suspectName ) )
    arrest( userName );

Note that using equals() is *not* the same as:

// if ( userName == suspectName )      // Wrong! 

The above code tests to see if the two String objects are the same object, which is sufficient but not necessary for them to be equivalent objects.

A class should override the equals() method if it needs to implement its own notion of equality. If you have no need to compare objects of a particular class, you don't need to override equals().

Watch out for accidentally overloading equals() when you mean to override it. equals() takes an Object as an argument and returns a boolean value. While you'll probably want to check only if an object is equivalent to an object of its own type, in order to properly override equals(), the method should accept a generic Object as its argument. Here's an example of implementing equals():

class Sneakers extends Shoes { 
    public boolean equals( Object arg ) { 
        if ( (arg != null) && (arg instanceof Sneakers) ) { 
            // compare arg with this object to check equivalence 
            // If comparison is okay... 
            return true; 
        } 
        return false; 
    } 
    ... 
} 

A Sneakers object can now be properly compared by any current or future Java classes. If we had instead used a Sneakers type object as the argument to equals(), all would be well for classes that reference our objects as Sneakers, but methods that simply use Shoes would not see the overloaded method and would compare Sneakers against other Sneakers improperly.

Hashcodes

The hashCode() method returns an integer that is a hashcode for a class instance. A hashcode is like a signature for an object; it's an arbitrary-looking identifying number that is (with important exceptions) generally different for different instances of the class. Hashcodes are used in the process of storing objects in a Hashtable, or a similar kind of collection. The hashcode is essentially an index into the collection. See Chapter 7, Basic Utility Classes for a complete discussion of Hashtable objects and hashcodes.

The default implementation of hashCode() in Object assigns each object instance a unique number to be used as a hashcode. If you don't override this method when you create a new class, each instance of the class will have a unique hashcode. This is sufficient for most objects. However, if the class has a notion of equivalent objects, then you should probably override hashCode() so that equivalent objects are given the same hashcode.

java.lang.Class

The last method of Object we need to discuss is getClass(). This method returns a reference to the Class object that produced the object instance.

A good measure of the complexity of an object-oriented language is the degree of abstraction of its class structures. We know that every object in Java is an instance of a class, but what exactly is a class? In C++, objects are formulated by and instantiated from classes, but classes are really just artifacts of the compiler. Thus, you see only classes mentioned in C++ source code, not at run-time. By comparison, classes in Smalltalk are real, run-time entities in the language that are themselves described by "meta-classes" and "meta-class classes." Java strikes a happy medium between these two languages with what is, effectively, a two-tiered system that uses Class objects.

Classes in Java source code are represented at run-time by instances of the java.lang.Class class. There's a Class object for every class you use; this Class object is responsible for producing instances for its class. This may sound overwhelming, but you don't have to worry about any of it unless you are interested in loading new kinds of classes dynamically at run-time.

We can get the Class associated with a particular object with the getClass() method:

String myString = "Foo!"
Class c = myString.getClass();

We can also get the Class reference for a particular class statically, using the special .class notation:

Class c = String.class;

The .class reference looks like a static field that exists in every class. However, it is really resolved by the compiler.

One thing we can do with the Class object is to ask for the name of the object's class:

String s = "Boofa!"; 
Class strClass = s.getClass(); 
System.out.println( strClass.getName() ); // prints "java.lang.String" 

Another thing that we can do with a Class is to ask it to produce a new instance of its type of object. Continuing with the above example:

try { 
    String s2 = (String)strClass.newInstance(); 
} 
catch ( InstantiationException e ) { ... } 
catch ( IllegalAccessException e ) { ... } 

newInstance() has a return type of Object, so we have to cast it to a reference of the appropriate type. A couple of problems can occur here. An InstantiationException indicates we're trying to instantiate an abstract class or an interface. IllegalAccessException is a more general exception that indicates we can't access a constructor for the object. Note that newInstance() can create only an instance of a class that has an accessible default constructor. There's no way for us to pass any arguments to a constructor.

All this becomes more meaningful when we add the capability to look up a Class by name. forName() is a static method of Class that returns a Class object given its name as a String:

try { 
    Class sneakersClass = Class.forName("Sneakers"); 
}  
catch ( ClassNotFoundException e ) { ... } 

A ClassNotFoundException is thrown if the class can't be located.

Combining the above tools, we have the power to load new kinds of classes dynamically. When combined with the power of interfaces, we can use new data types by name in our applications:

interface Typewriter { 
    void typeLine( String s ); 
    ... 
} 
 
class Printer implements Typewriter { 
    ... 
} 
 
class MyApplication { 
    ... 
    String outputDeviceName = "Printer"; 
 
    try { 
        Class newClass = Class.forName( outputDeviceName ); 
        Typewriter device = (Typewriter)newClass.newInstance(); 
        ... 
        device.typeLine("Hello..."); 
    } 
    catch ( Exception e ) { 
} 

5.11 Reflection

In this section we'll take a look at the Java reflection API, supported by the classes in the java.lang.reflect package. As its name suggests, reflection is the ability for a programming language to examine itself. The Java reflection API lets Java code look at an object (more precisely, the class of the object) and determine its structure. Within the limits imposed by the security manager, you can find out what constructors, methods, fields a class has, and their attributes. You can even change the value of fields, dynamically invoke methods, and construct new objects, much as if Java had primitive pointers to variables and methods.

We don't have room here to cover the full reflection API. As you might expect, the reflect package is complex and rich in details. But reflection has been designed so that you can do a lot with relatively little effort; 20% of the effort will give you 80% of the fun.

The reflection API is used by Java Beans to determine the capabilities of objects at runtime. It's also used at a lower level by object serialization (see Chapter 8) to tear apart and build objects for transport over streams or into persistent storage. Obviously, the power to pick apart objects and see their internals must be zealously watched by the security manager. Your code is not allowed to do anything with the reflection API that it couldn't do with static Java code. In short, reflection is a powerful tool, but it isn't a loophole. An object can't use it to find out about data fields that it wouldn't normally be able to access (for example, another object's private fields), and you can't use it to modify any data inappropriately.

The three primary features of a class are its fields (variables), its methods, and its constructors. For purposes of describing or accessing an object, these three features are represented by the classes in the reflection API: the java.lang.reflect.Field, java.lang.reflect.Method, and java.lang.reflect.Constructor classes represent the fields, methods, and constructors of a class. To get one of these objects, we use the class's Class.

Field[] getFields()

Get the public variables, including inherited ones.

Field getField(String name)

Get the specified public variable, which may be inherited.

Field[] getDeclaredFields()

Get all, public and nonpublic, variables declared in this class (not including those inherited from superclasses).

Field getDeclaredField(String name)

Get the specified variable, public or nonpublic, declared in this class (inherited variables not considered).

Method[] getMethods()

Get the public methods, including inherited ones.

Method getMethod(String name, Class [] argumentTypes)

Get the specified public method, who's arguments match the types listed in argumentTypes. The method may be inherited.

Method[] getDeclaredMethods()

Get all, public and nonpublic, methods declared in this class (not including those inherited from superclasses).

Method getDeclaredMethod(String name, Class[] argumentTypes)

Get the specified method, public or nonpublic, who's arguments match the types listed in argumentTypes, and which is declared in this class (inherited methods not considered).

Constructor[] getConstructors()

Get the public constructors of this class.

Constructor getConstructor(Class[] argumentTypes)

Get the specified public constructor of this class, who's arguments match the types listed in argumentTypes.

Constructor[] getDeclaredConstructors()

Get all, public and nonpublic, constructors of this class.

Constructor getDeclaredConstructor(Class[] argumentTypes)

Get the specified constructor, public or nonpublic, who's arguments match the types listed in argumentTypes.

The table above shows that the Class class provides two pairs of methods for getting at each type of feature. One pair allows access to a class's public features (including those inherited from its superclases), while the other pair allows access to any public or nonpublic item declared within the class (but not features that are inherited), subject to security considerations. For example, getFields() returns an array of Field objects representing all of a class's public variables, including those it inherits. getDeclaredFields() returns an array representing all the variables declared in the class, regardless of their access modifiers (not including variables the security manager won't let you see), but not including inherited variables. (For constructors, the distinction between "all constructors" and "declared constructors" is meaningful, so getConstructors() and getDeclaredConstructors() differ only in that the former returns public constructors, while the latter returns all the class's constructors.) Each pair of methods includes a method for listing all of the items at once (for example, getFields()), and a method for looking up a particular item by name and (for methods and constructors) signature (for example, getField(), which takes the field name as an argument).

As a quick example, we'll show how easy it is to list all of the public methods of the java.util.Calendar class:

Method [] methods = Calendar.class.getMethods();
for (int i=0; i < methods.length; i++)
    System.out.println( methods[i] );

Here we have used the .class notation to get a reference the Class of Calendar. Remember the discussion of the Class class--the reflection methods don't belong to the Calendar class itself; they belong to the java.lang.Class object that describes the Calendar class. If we wanted to start from an instance of Calendar (or, say, an unknown object) we could have used the getClass() method of the object instead:

Method [] methods = myUnknownObject.getClass().getMethods();

Security

Access to the reflection API is governed by a security manager. A fully trusted application has access to all of the above functionality--it can gain access to members of classes at the level of restriction normally granted code within its scope. There is currently no "special" access granted by the reflection API. It is possible that in the future, the full power of the reflection API will be available to completely trusted code such as debuggers; right now, user code can only see what it could have seen at compile-time. Untrusted code (for example, an unsigned applet) has the normal level of access to classes loaded from its own origin (classes sharing its classloader), but can only rely on the ability to access the public members of public classes coming from the rest of the system.

Accessing Fields

The class java.lang.reflect.Field is used to represent static variables and instance variables. Field has a full set of accessor methods for all of the base types (for example, getInt() and setInt(), getBoolean() and setBoolean()), and get() and set() methods for accessing members that are object references.

For example, given the following class:

class BankAccount {
    public int balance;
}

With the reflection API we can read and modify the value of the public integer field balance:

BankAccount myBankAccount = ...;
...
try {
    Field balanceField = BankAccount.class.getField("balance");
    int balance = balanceField.getInt( myBankAccount );  // read it
    balanceField.setInt( myBankAccount, 42 );            // change it
} catch ( NoSuchFieldException e ) { 
    // There is no "balance" field in this class
} catch ( IllegalAccessException e2) {
    // We don't have permission to access the field.
}

The various methods of Field take a reference to the particular object instance that we want to access. In the code above, the getField() method returns a Field object that represents the balance of the BankAccount class; this object doesn't refer to any specific BankAccount. Therefore, to read or modify any specific BankAccount, we call getInt() and setInt() with a reference to myBankAccount, which is the account we want to work with. As you can see, an exception occurs if we ask for access to a field that doesn't exist, or if we don't have the proper permission to read or write the field. If we make balance a private field, we can still look up the Field object that describes it, but we won't be able to read or write its value.

Therefore, we aren't doing anything that we couldn't have done with static code at compile-time; as long as balance is a public member of a class that we can access, we can write code to read and modify its value. What's important is that we're accessing balance at run-time, and could use this technique to examine the balance field in a class that was dynamically loaded.

Accessing Methods

The class java.lang.reflect.Method represents a static or instance method. Subject to the normal security rules, a Method object's invoke() method can be used to call the underlying object's method with specified arguments. Yes, Java has something like a method pointer!

As an example, we'll write a Java application called invoke that takes as command line arguments the name of a Java class and the name of a method to invoke. For simplicity, we'll assume that the method is static and takes no arguments:

import java.lang.reflect.*;
class invoke {
    public static void main( String [] args ) {
        try {
            Class c = Class.forName( args[0] );
            Method m = c.getMethod( args[1], new Class [] { } );
            Object ret =  m.invoke( null, null );
            System.out.println( "Invoked static method: " + args[1] + 
                " of class: " + args[0] + " with no args\nResults: " + ret );
        } catch ( ClassNotFoundException e ) {
            // Class.forName() can't find the class
        } catch ( NoSuchMethodException e2 ) {
            // that method doesn't exist
        } catch ( IllegalAccessException e3 ) {
            // we don't have permission to invoke that method
        } catch ( InvocationTargetException e4 ) {
            // an exception ocurred while invoking that method
            System.out.println("Method threw an: " + e4.getTargetException() );
        }
    }
}

We can run invoke to fetch the value of the system clock:

% java invoke java.lang.System currentTimeMillis
Invoked static method: currentTimeMillis of class: java.lang.System
with no args 
Results: 861129235818

Cool, eh? Maybe you'll consider this the first step towards writing a full blown scripting language for Java, in Java. If you do, please let me know.

Turning to the code, our first task is to look up the specified Class by name. To do so, we call the forName() method with the name of the desired class (the first command line argument). We then ask for the specified method by its name. getMethod() has two arguments: the first is the method name (the second command line argument) and the second is an array of Class objects that specifies the method's signature. (Remember that any method may be overloaded; you must specify the signature to make it clear which version you want.) Since our simple program only calls methods with no arguments, we create an anonymous empty array of Class objects. Had we wanted to invoke a method that takes arguments, we would have passed an array of the classes of their respective types, in the proper order. (An IllegalArgumentException can be thrown at run-time if they are wrong). The classes of primitive types can be found in the static TYPE fields of their respective wrappers; for example, use Integer.TYPE for the class of a primitive integer.

Once we have the Method object, we call its invoke() method. This calls our target method, and returns the result as an Object. (To do anything nontrivial with this object, you have to cast it to something more specific. Presumably, since you're calling the method, you know what kind of object to expect.) If the returned value is a primitive type like int or boolean, it will be wrapped in the standard wrapper class for its type. (Wrappers for primitive types are discussed in Chapter 7, Basic Utility Classes.) If the method returns void, invoke() returns a Void object. (This is why a wrapper class is needed for void; we need a class to represent void return values.)

The first argument to invoke() is the object on which we would like to invoke the method. If the method is static, there is no object, so we set the first argument to null. That's the case in our example. The second argument is an array of objects to be passed as arguments to the methods. The types of these should match the types specified in the getMethod() call. Because we're calling a method with no arguments, we can pass null for the second argument to invoke(). As with the return value, the types of primitive arguments are expected to be wrapped in wrapper classes. The reflection API automatically unpacks them for the method invocation.

The exceptions shown in the code above occur if we cannot find or don't have permission to access the method. Additionally, an InvocationTargetException occurs if the method being invoked throws some kind of exception itself. You can find out what it threw by calling the getTargetException() method of InvocationTargetException.

Accessing Constructors

The java.lang.reflect.Constructor class represents an object constructor. Subject to the security manager, you can use it to create a new instance of an object, with arguments. Although you can load new classes dynamically and create instances of them with Class.forName() and Class.newInstance(), you cannot specify arguments with those methods.

Here we'll create an instance of java.util.Date, passing a string argument to the constructor:

try {
    Constructor c = Date.class.getConstructor( new Class [] { String.class } );
    Object o = c.newInstance( new Object [] { "Jan 1, 1997" } );
    Date d = (Date)o;
    System.out.println(d);
} catch ( NoSuchMethodException e ) {
        // getConstructor() couldn't find the constructor we described
} catch ( InstantiationException e2 ) {
    // the class is abstract 
} catch ( IllegalAccessException e3 ) {
    // we don't have permission to create an instance
} catch ( InvocationTargetException e4 ) {
    // the construct threw an exception
}

The story is much the same as with a method invocation; after all, a constructor is really no more than a method with some strange properties. We look up the appropriate constructor for our Date class--the one that takes a single String as its argument--by passing getConstructor() an array containing the String class as its only element. (If the constructor required more arguments, we would put additional objects in the array, representing the classes of each argument.) We can then invoke newInstance(), passing it a corresponding array of argument objects. Again, to pass primitive types we would wrap them in their wrapper types first. Finally, we cast the resulting object to a Date, and print it.

The same exceptions seen in the previous example apply here, including the possible IllegalArgumentException. In addition, newInstance() throws an InstantiationException if the class is abstract and cannot be instantiated.

What about arrays?

The reflection API allows you to create and inspect array of base types using the java.lang.reflect.Array class. The process is much the same as with the other classes. For more information, look in a language reference.

What is Reflection good for?

Well, we've already said that reflection is used by the serialization process (Chapter 8, Input/Output Facilities), and that it is used by graphical development environments, like Borland's JBuilder and Symantec's Visual Cafe. But these are pretty much behind the scenes applications. What can reflection do for the average Java programmer?

First, it's there when you really need a method pointer. However, in most situations where a method pointer is convenient, there are other solutions to try; writing an anonymous adapter class is likely to be clearer and more efficient. Reflection would let you write a generic adapter class; that is, an adapter that doesn't know in advance what method to call. The adapter's client could pass a method name to the adapter as a string; the adapter would then use reflection to find the given Method in its client.

Even more generally, you can use reflection in any situation where you need to call methods that you don't know about in advance. It's hard to think of good examples--but then, that's the beauty of Java: it opens possibilities for new kinds of applications. Perhaps you'll need to write some kind of self-extending program, that loads new modules dynamically and uses reflection to find out how to use them. In short, don't relegate reflection to the dusty toolbox of tricks that are only useful for experts. With some experimentation, you'll find that reflection greatly adds to Java's capabilities.

 

to previous section to next section