Chapter 7. Working with Objects and Classes

In the previous two chapters, we came to know Java objects and their interrelationships. We will now climb the scaffolding of the Java class hierarchy to the very top and finish our study of the core language at the summit. In this chapter, we’ll talk about the Object class itself, which is the “grandmother” of all classes in Java. We’ll also describe the even more fundamental Class class (the class named “Class”) that represents Java classes in the Java virtual machine. We’ll discuss what you can do with these components in their own right. This will lead us to a more general topic: the Java Reflection API, which lets a Java program inspect and interact with (possibly unknown) objects dynamically at runtime. Finally, we’ll also talk about the Java Annotations API, which allows developers to add metadata to their source code for use by the compiler and runtime systems that look for it.

The Object Class

java.lang.Object is the ancestor 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 every class, throughout all of Java. At last count, there were nine public methods and two protected 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 9. The remaining four methods are used for basic comparison, conversion, and administration.

Every object has a toString() method that can be called when it’s to be represented as a text value. PrintStream objects use toString() to print data, as discussed in Chapter 12. toString() is also used implicitly 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 can override toString() and implement its own version that provides appropriate information about itself. This is particularly helpful in debugging, where it is common to print the string value of an object to see what is going on. Two other methods, equals() and hashCode(), may also require specialization when you create a new class.

Equality and Equivalence

equals() determines 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 );

Using equals() is not the same as the “==” operator in Java:

    if ( userName == suspectName )      // Wrong!

This statement tests whether the two reference variables, userName and suspectName, refer to the same object. It is a test for identity, not equality. Two variables that are identical (point to the same object) will necessarily be equal, but the converse is not always true. It is possible in Java to construct two String objects with the same contents that are, nonetheless, different instances of the String class—although, as we’ll describe later, Java tries to help you avoid that when it can.

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 necessarily need to override equals().

Watch out for accidentally overloading equals() if you mean to override it. With overloading, the method signatures differ; with overriding, they must be the same. The equals() method signature specifies an Object argument so that an object can be compared to any other kind of object, not only those of its own class type. You’ll probably want to consider only objects of the same type for equivalence. But in order to override (not overload) equals(), the method must specify its argument to be an Object.

Here’s an example of correctly overriding an equals() method in class Shoes with an equals() method in subclass Sneakers. Using its own method, a Sneakers object can compare itself with any other object:

    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;
        }
        ...
    }

If we specified public boolean equals(Sneakers arg) ... in the Sneakers class, we’d overload the equals() method instead of overriding it. If the other object happens to be assigned to a non-Sneakers variable, the method signature won’t match. The result: superclass Shoes’s implementation of equals() is called, which may or may not be what you intended.

Hashcodes

The hashCode() method returns an integer that is a hashcode for the object. A hashcode is like a signature or checksum for an object; it’s a random-looking identifying number that is usually generated from the contents of the object. The hashcode should always be different for instances of the class that contain different data, but should be the same for instances that compare “equal” with the equals() method. Hashcodes are used in the process of storing objects in a Hashtable or a similar kind of collection. (A Hashtable is sometimes called a dictionary or associative array in other languages.) A random distribution of the hashcode values helps the Hashtable optimize its storage of objects by serving as an identifier for distributing them into storage evenly and quickly locating them later.

The default implementation of hashCode() in Object does not really implement this scheme. Instead it assigns each object instance a unique number. If you don’t override this method when you create a subclass, each instance of your class will have a unique hashcode. This is sufficient for some objects. However, if your classes have a notion of equivalent objects (if you have overridden equals()) and you want equal objects to serve as equivalent keys in a Hashtable, you should override hashCode() so that your equivalent objects generate the same hashcode value. We’ll return to the topic of hashcodes in more detail in Chapter 11 when we discuss the Hashtable and HashMap classes.

Cloning Objects

Objects can use the clone() method of the Object class to make copies of themselves. A copied object is a new object instance, separate from the original. It may or may not contain exactly the same state (the same instance variable values) as the original; that is controlled by the object being copied. Just as important, the decision as to whether the object allows itself to be cloned at all is up to the object.

The Java Object class provides the mechanism to make a simple copy of an object including all of its “shallow” state—a bitwise copy. But by default, this capability is turned off. (We’ll show why in a moment.) To make itself cloneable, an object must implement the java.lang.Cloneable interface. This is a flag interface indicating to Java that the object wants to cooperate in being cloned (the interface does not actually contain any methods). If the object isn’t cloneable, the clone() method throws a CloneNotSupportedException.

clone() is a protected method, so by default it can be called only by an object on itself, an object in the same package, or another object of the same type or a subtype. If we want to make an object cloneable by everyone, we have to override its clone() method and make it public.

Here is a simple, cloneable class—Sheep:

    import java.util.HashMap;

    public class Sheep implements Cloneable {
        HashMap flock = new HashMap();

        public Object clone() {
            try {
                return super.clone();
            } catch (CloneNotSupportedException e ) {
                throw new Error(
                    "This should never happen because we implement Cloneable!");
            }
        }
    }

Sheep has one instance variable, a HashMap called flock (which the sheep uses to keep track of its fellow sheep). Our class implements the Cloneable interface, indicating that it is OK to copy Sheep, and it has overridden the clone() method to make it public. Our clone() simply returns the object created by the superclass’s clone() method—a copy of our Sheep. Unfortunately, the compiler is not smart enough to figure out that the object we’re cloning will never throw the CloneNotSupportedException, so we have to guard against it anyway. Our sheep is now cloneable. We can make copies like so:

    Sheep one = new Sheep();
    Sheep anotherOne = (Sheep)one.clone();

The cast is necessary here because the return type of clone() is Object. We can do better by changing the return type of the overridden clone() method in the subclass and moving the cast into the clone() method itself, to make things a little easier on the users of the class:

    public Sheep clone() {
        try {
            return (Sheep)super.clone();
        } catch (CloneNotSupportedException e ) {
            throw new Error("This should never happen!");
        }
    }

    // usage
    Sheep one = new Sheep();
    Sheep anotherOne = one.clone();

In either case, we now have two sheep instead of one. A properly implemented equals() method would tell us that the sheep are equivalent, but == tells us that they are, in fact, two distinct instances of Sheep. Java has made a shallow copy of our Sheep. What’s so shallow about it? Java has simply copied the values of our variables. That means that the flock instance variable in each of our Sheep still holds the same information—that is, both sheep have a reference to the same HashMap. The situation looks like that shown in Figure 7-1.

Shallow copy of an object
Figure 7-1. Shallow copy of an object

This may or may not be what you intended. If we instead want our Sheep to have separate copies of its full state (or something in between), we can take control ourselves. In the following example, DeepSheep, we implement a deep copy, duplicating our own flock variable:

    public class DeepSheep implements Cloneable {
        HashMap flock = new HashMap();

        public DeepSheep clone() {
            try {
                DeepSheep copy = (DeepSheep)super.clone();
                copy.flock = (HashMap)flock.clone();
                return copy;
            } catch (CloneNotSupportedException e ) {
                throw new Error("This should never happen!");
            }
        }
    }

Our clone() method now clones the HashMap as well. Now, when a DeepSheep is cloned, the situation looks more like that shown in Figure 7-2.

Each DeepSheep now has its own full copy of the map, which can contain different elements. You can see now why objects are not cloneable by default. It would make no sense to assume that all objects can be sensibly duplicated with a shallow copy. Likewise, it makes no sense to assume that a deep copy is necessary, or even correct. In this case, we probably don’t need a deep copy; the flock contains the same members no matter which sheep you’re looking at, so there’s no need to copy the HashMap. But the decision depends on the object itself and its requirements.

Deep copy of an object
Figure 7-2. Deep copy of an object

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. We’ll talk about it next.

The Class Class

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 languages like traditional C++, objects are formulated by and instantiated from classes, but classes are really just artifacts of the compiler. In those languages, you see classes mentioned only in source code, not at runtime. By comparison, classes in Smalltalk are real, runtime entities in the language that are themselves described by “metaclasses” and “metaclass 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 runtime by instances of the java.lang.Class class. There’s a Class object for every object type you use; this Class object is responsible for producing instances of that type. But you don’t generally have to worry about that unless you are interested in loading new kinds of classes dynamically at runtime or using a highly abstracted API that wants a “type” instead of an actual argument. The Class object is also the basis for “reflecting” on a class to find its methods and other properties, allowing you to find out about an object’s structure or invoke its methods programmatically at runtime. We’ll discuss reflection in the next section.

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

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

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

    Class stringClass = 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 ask for its full name:

    String s = "Boofa!";
    Class stringClass = s.getClass();
    System.out.println( stringClass.getName() );   // "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 previous example:

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

Here, newInstance() has a return type of Object, so we have to cast it to a reference of the appropriate type. This is fine, but we’ll see in the next chapter that the Class class is a generic class, which means that we can parameterize it to be more specific about the Java type we’re dealing with; that is, we can get the newInstance() method to return the correct type directly without the cast. We’ll show this here, but don’t worry if it doesn’t make any sense yet:

    Class<String> stringClass = String.class;
    try {
        String s2 = stringClass.newInstance(); // no cast necessary
    }
    catch ( InstantiationException e ) { ... }
    catch ( IllegalAccessException e ) { ... }

A couple of exceptions can be thrown here. An InstantiationException indicates that we’re trying to instantiate an abstract class or an interface. IllegalAccessException is a more general exception that indicates that 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. It doesn’t allow us to pass any arguments to a constructor. (In the next section, we’ll learn how to do just that using the Reflection API.)

All of 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 these 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 loaded by a string 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 ) { ... }
    }

Here, we have an application loading a class implementation (Printer, which implements the Typewriter interface) knowing only its name. Imagine the name was entered by the user or looked up from a configuration file. This kind of class loading is the basis for many kinds of configurable systems in Java.

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 class or object to examine itself. Reflection 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, and fields a class has, as well as 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. And you can do all this on objects that your code has never even seen before. The Annotations API also has the ability to preserve metadata about source code in the compiled classes and we can retrieve this information with the Reflection API.

We don’t have room here to cover the Reflection API fully. 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 gives you 80% of the fun.

The Reflection API can be used to determine the capabilities of objects at runtime. It’s used by object serialization 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 guarded by the security manager. The general rule is that your code is not allowed to do anything with the Reflection API that it couldn’t do with static (ordinary, compiled) Java code. In short, reflection is a powerful tool, but it isn’t an automatic loophole. By default, an object can’t use it to work with fields or methods that it wouldn’t normally be able to access (for example, another object’s private fields), although those privileges can be granted, as we’ll discuss later.

The three primary features of a class are its fields (variables), methods, and constructors. For purposes of describing and accessing an object, these three features are represented by separate classes in the Reflection API: java.lang.reflect.Field, java.lang.reflect.Method , and java.lang.reflect.Constructor . We can look up these members of a class through the Class object.

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 superclasses) while the other pair allows access to any public or nonpublic item declared directly within the class (but not features that are inherited), subject to security considerations. Some examples:

  • getFields() returns an array of Field objects representing all 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, but not including inherited variables.

  • For constructors, the distinction between “all constructors” and “declared constructors” is not meaningful (classes do not inherit constructors), so getConstructors() and getDeclaredConstructors() differ only in that the former returns public constructors while the latter returns all the class’s constructors.

Each set of methods includes the methods for listing all the items at once (for example, getFields()) and an additional method for looking up a particular item by name and—for methods and constructors—by signature (for example, getField(), which takes the field name as an argument).

The following listing shows the methods in the Class class:

Field [] getFields();

Get all 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 all public methods, including inherited ones.

Method getMethod(String name, Class ... argumentTypes);

Get the specified public method that has arguments that 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, that has arguments that match the types listed in argumentTypes, and which is declared in this class (inherited methods not considered).

Constructor [] getConstructors();

Get all public constructors of this class.

Constructor getConstructor(Class ... argumentTypes);

Get the specified public constructor of this class that has arguments that 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, that has arguments that match the types listed in argumentTypes.

Class [] getDeclaredClasses();

Get all public and nonpublic inner classes declared within this class.

Constructor [] getInterfaces();

Get all interfaces implemented by this class, in the order in which they are declared.

As you can see, the four getMethod() and getConstructor() methods take advantage of the Java variable-length argument lists to allow you to pass in the argument types. In older versions of Java, you have to pass an array of Class types in their place. We’ll show an example later.

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

    for ( Method method : Calendar.class.getMethods() )
        System.out.println( method );

Here, we’ve used the .class notation to get a reference to the Class of Calendar. Remember the discussion of the Class class; the reflection methods don’t belong to a particular instance of Calendar 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();

Modifiers and Security

All of the types of members of a Java class—fields, methods, constructors, and inner classes—have a method getModifiers() that returns a set of flags indicating whether the member is private, protected, default level, or publicly accessible. You can test for these with the java.lang.reflect.Modifier class, like so:

    Method method = Object.class.getDeclaredMethod( "clone" ); // no arguments
    int perms = method.getModifiers();
    System.out.println( Modifier.isPublic( perms ) ); // false
    System.out.println( Modifier.isProtected( perms ) ); // true
    System.out.println( Modifier.isPrivate( perms ) ); // false

In this example, the clone() method in Object is protected.

Access to the Reflection API is governed by a security manager. A fully trusted application has access to all the previously discussed functionality; it can gain access to members of classes at the level of restriction normally granted code within its scope. It is, however, possible to grant special access to code so that it can use the Reflection API to gain access to private and protected members of other classes in a way that the Java language ordinarily disallows.

The Field, Method, and Constructor classes all extend from a base class called AccessibleObject. The AccessibleObject class has one important method called setAccessible(), which allows you to deactivate normal security when accessing that particular class member. That may sound too easy. It is indeed simple, but whether that method allows you to disable security or not is a function of the Java security manager and security policy. You can do this in a normal Java application running without any security policy, but not, for example, in an applet or other secure environment. For example, to be able to use the protected clone() method of the Object class, all we have to do (given no contravening security manager) is:

    Method method = Object.class.getDeclaredMethod( "clone" );
    method.setAccessible( true );

Accessing Fields

The class java.lang.reflect.Field represents static variables and instance variables. Field has a full set of overloaded accessor methods for all the base types (for example, getInt() and setInt(), getBoolean() and setBoolean()), and get() and set() methods for accessing fields that are reference types. Let’s consider this 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");
        // read it
        int mybalance = balanceField.getInt( myBankAccount );
        // change it
        balanceField.setInt( myBankAccount, 42 );
    } catch ( NoSuchFieldException e ) {
        ... // there is no "balance" field in this class
    } catch ( IllegalAccessException e2) {
        ... // we don't have permission to access the field
    }

In this example, we are assuming that we already know the structure of a BankAccount object. In general, we could gather that information from the object itself.

All the data access methods of Field take a reference to the particular object instance that we want to access. In this example, 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 particular object instance that contains the field with which we want to work. For a static field, we’d use the value null here. An exception occurs if we try to access a field that doesn’t exist, or if we don’t have the proper permission to read or write to 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 runtime, and we could just as easily use this technique to examine the balance field in a class that was dynamically loaded or that we just discovered by iterating through the class’s fields with the getDeclaredFields() method.

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 does have 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 (quite a limitation):

    //file: Invoke.java
    import java.lang.reflect.*;

    class Invoke {
      public static void main( String [] args ) {
        try {
          Class clas = Class.forName( args[0] );
          Method method = clas.getMethod( args[1] ); // Named method, 
                                                     // no arguments
          Object ret =  method.invoke( null );  // Invoke a static method

          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 occurred 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

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.) Because our simple program calls only 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. For primitive types, we would have used the standard wrappers (Integer, Float, Boolean, etc.) to hold the values. The classes of primitive types in Java are represented by special static TYPE fields of their respective wrappers; for example, use Integer.TYPE for the Class of an int. As shown in comments in the code, starting in Java 5.0, the getMethod() and invoke() methods accept variable-length argument lists, which means that we can omit the arguments entirely and Java will make the empty array for us.

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 must cast it to something more specific. Presumably, because we’re calling the method, we know what kind of object to expect. But if we didn’t, we could use the Method getReturnType() method to get the Class of the return type at runtime. If the returned value is a primitive type such as int or boolean, it will be wrapped in the standard wrapper class for its type. If the method returns void, invoke() returns a java.lang.Void object. This is the “wrapper” class that represents 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 method. The types of these should match the types specified in the call to getMethod(). Because we’re calling a method with no arguments, we can pass null for the second argument to invoke(). As with the return value, you must use wrapper classes for primitive argument types.

The exceptions shown in the previous code occur if we can’t 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 what it threw by calling the getTargetException() method of InvocationTargetException.

Accessing Constructors

The java.lang.reflect.Constructor class represents an object constructor in the same way that the Method class represents a method. You can use it, subject to the security manager, of course, to create a new instance of an object, even with constructors that require arguments. Recall that you can create instances of a class with Class.newInstance(), but you cannot specify arguments with that method. This is the solution to that problem, if you really need to do it.

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

try {
        Constructor<Date> cons =
            Date.class.getConstructor( String.class );
        Date date = cons.newInstance( "Jan 1, 2006" );
        System.out.println( date );
    } 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() the String.class type. Here, we are using the Java 5.0 variable argument syntax. If the constructor required more arguments, we would pass additional Classes representing the class of each argument. We can then invoke newInstance(), passing it a corresponding argument object. Again, to pass primitive types, we would wrap them in their wrapper types first. Finally, we print the resulting object to a Date. Note that we’ve slipped in another strange construct using generics here. The Constructor<Date> type here simply allows us to specialize the Constructor for the Date type, alleviating the need to cast the result of the newInstance() method, as before.

The exceptions from the previous example apply here, too, along with IllegalArgumentException and InstantiationException. The latter is thrown if the class is abstract and, therefore, can’t be instantiated.

What About Arrays?

The Reflection API allows you to create and inspect arrays of base types using the java.lang.reflect.Array class. The process is very much the same as with the other classes, so we won’t cover it in detail. The primary feature is a static method of Array called newInstance(), which creates an array that allows you to specify a base type and length. You can also use it to construct multidimensional array instances by specifying an array of lengths (one for each dimension). For more information, look in your favorite Java language reference.

Accessing Generic Type Information

In Chapter 8, we’ll discuss generics, which first appeared in Java 5.0. Generics is a major addition that adds new dimensions (literally) to the concept of types in the Java language. With the addition of generics, types are no longer simply one-to-one with Java classes and interfaces but can be parameterized on one or more types to create a new, generic type. To make matters more complicated, these new types do not actually generate new classes, but instead are artifacts of the compiler. To keep the generic information, Java adds information to the compiled class files.

The Reflection API can accommodate all of this, mainly through the addition of the new java.lang.reflect.Type class, which is capable of representing generic types. Covering this in detail is a bit outside the scope of this book and because we won’t even get to generics until Chapter 8, we won’t devote much more space to this topic here. However, the following code snippets may guide you later if you return to the topic of accessing generic type information reflectively:

    // public interface List<E> extends Collection<E> { ... }

    TypeVariable [] tv = List.class.getTypeParameters();
    System.out.println( tv[0].getName() ); // "E"

This snippet gets the type parameter of the java.util.List class and prints its name:

    class StringList extends ArrayList<String> { }

    Type type = StringList.class.getGenericSuperclass();

    System.out.println( type );  //
    // "java.util.ArrayList<java.lang.String>"


    ParameterizedType pt = (ParameterizedType)type;
    System.out.println( pt.getActualTypeArguments()[0] ); //
    // "class java.lang.String"

This second snippet gets the Type for a class that extends a generic type and then prints the actual type on which it was parameterized.

Accessing Annotation Data

Later in this chapter, we discuss annotations, a feature that allows metadata to be added to Java classes, methods, and fields. Annotations can optionally be retained in the compiled Java classes and accessed through the Reflection API. This is one of several intended uses for annotations, allowing code at runtime to see the metadata and provide special services for the annotated code. For example, a property (field or setter method) on a Java object might be annotated to indicate that it is expecting a container application to set its value or export it in some way.

Covering this in detail is outside the scope of this book; however, getting annotation data through the Reflection API is easy. Java classes, as well as Method and Field objects, have the following pairs of methods (and some other related ones):

    public <A extends Annotation> A getAnnotation(Class<A> annotationClass)
    public Annotation[] getDeclaredAnnotations()

These methods (the first is a generic method, as covered in Chapter 8) return java.lang.annotation.Annotation type objects that represent the metadata.

Dynamic Interface Adapters

Ideally, Java reflection would allow us to do everything at runtime that we can do at compile time (without forcing us to generate and compile source into bytecode). But that is not entirely the case. Although we can dynamically load and create instances of objects at runtime using the Class.forName() method, there is no general way to create new types of objects—for which no class files preexist—on the fly.

The java.lang.reflect.Proxy class, however, takes a step toward solving this problem by allowing the creation of adapter objects that implement arbitrary Java interfaces at runtime. The Proxy class is a factory that can generate an adapter class, implementing any interface (or interfaces) you want. When methods are invoked on the adapter class, they are delegated to a single method in a designated InvocationHandler object. You can use this to create dynamic implementations of any kind of interface at runtime and handle the method calls anywhere you want. For example, using a Proxy, you could log all of the method calls on an interface, delegating them to a “real” implementation afterward. This kind of dynamic behavior is important for tools that work with Java beans, which must register event listeners. (We’ll mention this again in Chapter 22.) It’s also useful for a wide range of problems.

In the following snippet, we take an interface name and construct a proxy implementing the interface. It outputs a message whenever any of the interface’s methods are invoked:

    import java.lang.reflect.*;

    InvocationHandler handler =
        new InvocationHandler() {
            Object invoke( Object proxy, Method method, Object[] args ) {
                System.out.println(
                    "Method: {[QUOTE-REPLACEMENT]}+ method.getName() +"()"
                    +" of interface: "+ interfaceName
                    + " invoked on proxy!"
                );
                return null;
            }
        };

    Class clas = Class.forName( MyInterface );

    MyInterface interfaceProxy =
        (MyInterface)Proxy.newProxyInstance(
            clas.getClassLoader(), new Class[] { class }, handler );

    // use MyInterface
    myInterface.anyMethod(); // Method: anyMethod() ... invoked on proxy!

The resulting object, interfaceProxy, is cast to the type of the interface we want. It will call our handler whenever any of its methods are invoked.

First, we make an implementation of InvocationHandler. This is an object with an invoke() method that takes as its argument the Method being called and an array of objects representing the arguments to the method call. We then fetch the class of the interface that we’re going to implement using Class.forName(). Finally, we ask the proxy to create an adapter for us, specifying the types of interfaces (you can specify more than one) that we want implemented and the handler to use. invoke() is expected to return an object of the correct type for the method call. If it returns the wrong type, a special runtime exception is thrown. Any primitive types in the arguments or in the return value should be wrapped in the appropriate wrapper class. (The runtime system unwraps the return value, if necessary.)

What Is Reflection Good For?

Reflection, although in some sense a “back door” feature of the Java language, is finding more and more important uses. In this chapter, we mentioned that reflection is used to access runtime annotations. In Chapter 22, we’ll see how reflection is used to dynamically discover capabilities and features of JavaBean objects. Those are pretty specialized applications—what can reflection do for us in everyday situations?

We could use reflection to go about acting as if Java had dynamic method invocation and other useful capabilities; in Chapter 22, we’ll see a dynamic adapter class that uses reflection to make calls for us. As a general coding practice however, dynamic method invocation is a bad idea. One of the primary features of Java (and what distinguishes it from some similar languages) is its strong type safety. You abandon much of that when you take a dip in the reflecting pool. And although the performance of the Reflection API is very good, it is not precisely as fast as compiled method invocations in general.

More appropriately, you can use reflection in situations where you need to work with objects that you can’t know about in advance. Reflection puts Java on a higher plane of programming languages, opening up possibilities for new kinds of applications. As we’ve mentioned, it makes possible one use of Java annotations at runtime, allowing us to inspect classes, methods, and fields for metadata. Another important and growing use for reflection is integrating Java with scripting languages. With reflection, you can write language interpreters in Java that can access the full Java APIs, create objects, invoke methods, modify variables, and do all the other things a Java program can do at compile time, while the program is running. In fact, you could go so far as to reimplement the Java language in Java, allowing completely dynamic programs that can do all sorts of things. Sound crazy? Well, someone has already done this—one of the authors of this book! We’ll explain next.

The BeanShell Java scripting language

I (Pat) can’t resist inserting a plug here for BeanShell—my free, open source, lightweight Java scripting language. BeanShell is just what we alluded to in the previous section—a Java application that uses the Reflection API to execute Java statements and expressions dynamically. You can use BeanShell interactively to quickly try out some of the examples in this book or take it further to start using Java syntax as a scripting language in your applications. BeanShell exercises the Java Reflection API to its fullest and serves as a demonstration of how dynamic the Java runtime environment really is.

You can find a copy of BeanShell at its own website. See Appendix B for more information on getting started. We hope you find it both interesting and useful!

Annotations

As we mentioned in Chapter 4, Java for a long time has supported a limited kind of metadata in Java source code through the use of Javadoc comment tags. With Javadoc tags like @deprecated or @author, we can add some information to a class, method, or field by sticking it into comments above the item. In this case, the information is mainly useful to the Javadoc documentation generator, because comments exist only in Java source code. However, developers have long wanted a way to generalize metadata for other purposes. And in fact, some tools have been developed over the years that read extended Javadoc-style tags in comments and do all sorts of things with them, including code generation and documentation. In Java 5.0, a formal, extensible metadata system called annotations was added to the language that provides this kind of source-level functionality as well as new possibilities for using metadata at runtime.

Annotations allow you to add metadata to Java packages, classes, methods, and fields. This metadata can be utilized by tools at compile time and optionally retained in the compiled Java classes for use at runtime as well. The availability of annotation data to the running program opens up new uses for metadata. For example, annotations cannot only be used at compile time to generate auxiliary classes or resources, but also could be used by a server to provide special services to classes such as importing or exporting of values, security, or monitoring. Annotations will be used heavily in Java XML Binding (JAXB), the Java Servlets API, and Java Web Services (JAX-WS), as we’ll see later in the book. In those cases, annotations are used to simplify configuration and deployment information.

Technically, according to the spec, annotations are not supposed to “directly affect the semantics of a program.” However, that admonition is a little vague and there is some fear in the Java community that this facility will open a Pandora’s box of possible abuses. Hopefully, developers will use them with restraint.

Only a handful of “built-in” annotations are commonly used in Java and we’ll summarize them in this section. More built-in annotations are used with specialized packages such as those for web services and some are used in creating the annotations themselves. Creating your own annotations for use in your code is syntactically easy (essentially just like declaring an interface), but implementing the behavior for them via the compiler or a runtime system is a bit beyond the scope of this book, so we won’t cover that here. The JDK provides a framework tool called apt that can be used to implement source-level annotations that generate and compile code or resource files at compile time. Accessing annotation data at runtime is done via the Reflection API as described briefly earlier in this chapter.

Using Annotations

Annotations are placed in the code preceding the annotated item using an @ (at) symbol followed by the annotation class name. The @Deprecated annotation is an example of the simplest kind, a marker or flag annotation. A marker annotation indicates some semantics just by its presence. (In the case of @Deprecated, it means that the member is deprecated and the compiler should generate warnings if it is used.) To use the @Deprecated annotation, we place it before a Java class, method, or field like this:

    @Deprecated
    class OldClass { ... }

    class AgingClass 
    {
        @Deprecated 
        public someMethod() { ... }
        ...
    }

More generally, annotations may take “arguments” in an extended method-like syntax. Table 7-1 summarizes the possible variations.

Table 7-1. Use of arguments in annotations

Example

Description

@Deprecated

Marker annotation (no “data”)

@WarningMessage("Something about...")

Single argument

@TestValues( { "one", "two" } )

Array of arguments

@Author( first="Pat", last="Niemeyer" )

Named arguments

The first annotation in the table, @Deprecated, is a real annotation as described earlier; the remaining three are fictitious. To accept multiple values, an annotation may either use the curly brace ({}) array syntax or the more novel named argument syntax listed in the final example. The named syntax allows arguments to be passed in any order.

Package annotations

In the introduction, we mentioned that Java packages can be annotated. This raises the question of where one would place such an annotation, as there is ordinarily no location where we “declare” a Java package; we normally just use them implicitly. The answer is that by convention we can create a file named package-info.java and place it into the folder corresponding to the Java package. The file cannot contain Java classes, but should contain a package statement. Package annotations can be placed on this package statement. In the following example, we deprecate the whole package learningjava.oldstuff such that using any of its classes generates the deprecation warning.

// file: learningjava/service/package-info.java

/** 
 * We can put package comments here too! 
 */
@Deprecated
package learningjava.oldstuff;

Standard Annotations

Table 7-2 summarizes common annotations supplied with Java.

Table 7-2. Standard annotations

Annotation

Description

@Deprecated

Deprecation warning on member

@Override

Indicates that the annotated method must override a method in the parent class or else a compiler warning is issued

@SuppressWarnings(value=" type ")

Indicates that the specified warning types should be suppressed by the compiler for the annotated class or method

We have already discussed the @Deprecated and @Override annotations, the latter of which we covered in the section “Overriding Methods”. The @SuppressWarnings annotation is intended to have a compelling use in bridging legacy code with newer code using generics after Java 5.0, but some compilers may not implement it.

Additional annotations are supplied with Java as part of the java.lang.annotations package that are used to annotate only other annotations (they are really meta-annotations). For example, the java.lang.annotation.Retention annotation sets the retention policy for an annotation, specifying whether it is retained in the compiled class and loaded at runtime.

The apt Tool

The Java JDK ships with the command-line Annotation Processing Tool, apt, which is a sort of frontend to the javac compiler. apt uses pluggable annotation processors to process the annotations in source files before the code is compiled by javac. If you write your own source-level annotations, you can build a plug-in annotation processor for apt that will be invoked to process your annotations in the source code. Your annotation processor can be quite sophisticated, examining the structure of the source code (in a read-only fashion) through the supplied syntax tree (object model) and generating any additional files or actions that it wishes. If you generate new Java source files, they will be automatically compiled by javac for you. Running apt on a source file with no annotations simply falls through to javac.



[20] This Date constructor is deprecated but will serve us for this example.