Chapter 16. Reflection

 

A sense of humor keen enough to show a man his own absurdities will keep him from the commission of all sins, or nearly all, save those that are worth committing.

 
 --Samuel Butler

The package java.lang.reflect contains the reflection package, the classes you can use to examine a type in detail. You can write a complete type browser using these classes, or you can write an application that interprets code that a user writes, turning that code into actual uses of classes, creation of objects, invocations of methods, and so on. Almost all the types mentioned in this discussion on reflection are contained in the package java.lang.reflect, except for the classes Class and Package, which are part of the package java.lang, and Annotation, which is part of the java.lang.annotation package.

Reflection starts with a Class object. From the Class object you can obtain a complete list of members of the class, find out all the types of the class (the interfaces it implements, the classes it extends), and find out information about the class itself, such as the modifiers applied to it (public, abstract, final, and so on) or the package it is contained in. Reflection is also sometimes called introspection; both terms use the metaphor of asking the type to look at itself and tell you something. These capabilities can be used by type browsers to show the structure of an application. They also form the first step for you to dynamically create and manipulate objects.

Here's a simple example of a “type browser.” Given the name of a class this program shows the class's immediate superclass and lists all the public methods it declares:

import java.lang.reflect.*;
import static java.lang.System.out;
import static java.lang.System.err;

public class SimpleClassDesc {
    public static void main(String[] args) {
        Class type = null;
        try {
            type = Class.forName(args[0]);
        } catch (ClassNotFoundException e) {
            err.println(e);
            return;
        }

        out.print("class " + type.getSimpleName());
        Class superclass = type.getSuperclass();
        if (superclass != null)
            out.println(" extends " +
                        superclass.getCanonicalName());
        else
            out.println();

        Method[] methods = type.getDeclaredMethods();
        for (Method m : methods)
            if (Modifier.isPublic(m.getModifiers()))
                out.println("  " + m);
    }
}

Given the fully qualified name of a class (such java.lang.String) the program first tries to obtain a Class object for that class, using the static method Class.forName. If the class can't be found an exception is thrown which the program catches and reports. Otherwise, the simple name of the class—String, for example—is printed. Next the Class object is asked for its superclass's Class object. As long as the named class is not Object and is a class rather than an interface, getSuperclass will return a superclass's Class object. The name of the super class is printed in full using getCanonicalName (there are a number of ways to name a class as you'll see in Section 16.1.4 on page 411). Next, the Class object is asked for all the methods that it declares. The declared methods include all methods actually declared in the class but none that are inherited. Since we're only interested in public methods, we ask the Method object for the method's modifiers and ask the Modifier class if those modifiers include public. If so the method is printed, otherwise it is ignored. Here's the output when given the name of the Attr class from page 76:

class Attr extends java.lang.Object
  public java.lang.String Attr.getName()
  public java.lang.String Attr.toString()
  public java.lang.Object Attr.getValue()
  public java.lang.Object Attr.setValue(java.lang.Object)

Over the next few pages you'll see how to perform a much more detailed examination of a class's supertypes and its members.

Reflection also allows you to write code that performs actions that you can more simply execute directly in code if you know what you are doing. Given the name of a class (which may not have existed when the program was written) you can obtain a Class object and use that Class object to create new instances of that class. You can interact with those objects just as you would with an object created via new. You can, for example, invoke a method using reflection, as you will soon see, but it is much harder to understand than a direct method invocation. You should use reflection only when you have exhausted all other object-oriented design mechanisms. For example, you should not use a Method object as a “method pointer,” because interfaces and abstract classes are better tools. There are times when reflection is necessary—usually when you're interpreting or displaying some other code—but use more direct means whenever possible.

Figure 16-1 (see next page) shows the classes and interfaces that support introspection. At the top, Type represents all types that can exist in a Java program—it is a marker interface containing no methods. All concrete types are represented by an instance of class Class. The other types pertain to generic types: parameterized types, type variables, wildcards, and generic arrays. The Constructor, Method, and Field classes represent constructors, methods and fields, respectively. These are all Member instances and are also subclasses of AccessibleObject (which pertains to access control). The classes Class, Constructor, and Method are all GenericDeclaration instances because they can be declared in a generic way. Finally, the class Class, the AccessibleObject subclasses, and the Package class are all instances of AnnotatedElement because they can have annotations applied to them.

The Introspection Hierarchy

Figure 16-1. The Introspection Hierarchy

The Class Class

There is a Class object for every type. This includes each class, enum, interface, annotation, array, and the primitive types. There is also a special Class object representing the keyword void. These objects can be used for basic queries about the type and, for the reference types, to create new objects of that type.

The Class class is the starting point for reflection. It also provides a tool to manipulate classes, primarily for creating objects of types whose names are specified by strings, and for loading classes using specialized techniques, such as across the network. We look in more detail at class loading in Section 16.13 on page 435.

You get a Class object in four ways: ask an object for its Class object using its getClass method; use a class literal (the name of the class followed by .class, as in String.class); look it up by its fully qualified name (all packages included) using the static method Class.forName; or get it from one of the reflection methods that return Class objects for nested classes and interfaces (such as Class.getClasses).

Type Tokens

Class is a generic class, declared as Class<T>. Each Class object for a reference type is of a parameterized type corresponding to the class it represents. For example, the type of String.class is Class<String>, the type of Integer.class is Class<Integer>, and so forth. The type of the Class object for a primitive type is the same type as that of its corresponding wrapper class. For example, the type of int.class is Class<Integer>, but note that int.class and Integer.class are two different instances of the same type of class. Parameterized types all share the Class object of their raw type—for example, the Class object for List<Integer> is the same as that for List<String> and the same as that for List.class, and its type is Class<List>.

A parameterized Class type is known as the type token for a given class. The easiest way to obtain a type token is to use a class literal, such as String.class, since this provides you with the exact type token. In contrast, Class.forName is declared to return a wildcard, Class<?>, that represents an unidentified type token—to use this type token effectively you need to establish its actual type, as you'll soon see. The Object method getClass returns the type token for the type of object it is invoked on, and its return type is also Class<?>, another wildcard. However, getClass receives special treatment by the compiler: If getClass is invoked on a reference with static type T, then the compiler treats the return type of getClass as being Class<?extends T>.[1] So this works:

String str = "Hello";
Class<String> c1 = String.class;  // exact type token
Class<? extends String> c2 =
    str.getClass();               // compiler magic

but this won't compile:

Class<? extends String> c3 =
    Class.forName("java.lang.String");  // INVALID

Taking an unknown type token Class<?> and turning it into a type token of a known type is a common action when working with reflection. What you need is something that acts like a cast, but as you already know, you can't perform casts involving parameterized types. The solution to this is another piece of “magic” in the shape of the asSubclass method of class Class:

  • public <T> Class<? extends T> asSubclass(Class<T> subType)

    • Returns the Class object on which it is invoked, after casting it to represent a subclass of the given Class object. If the current Class object does not represent a type that is a subtype of subType's type (or subType itself), then ClassCastException is thrown.

The asSubclass method doesn't change the Class object at all; it simply changes the type of the expression it is invoked on, so we can use the additional type information—just as a cast does. For example, here's how you can use asSubclass with forName to fix the previous problem:

Class<? extends String> c3 =
    Class.forName("java.lang.String").
        asSubclass(String.class);       // OK

Note that you can't convert the unknown type token to be exactly Class<String>, but having Class<?extends String> should be sufficient. Of course, String is not a practical example for using reflection. A more typical example would be when you are loading an unknown class that implements a known interface and you want to create an instance of that class; you'll see an example of this in the Game class on page 436.

Class Inspection

The Class class provides a number of methods for obtaining information about a particular class. Some of these provide information on the type of the class—the interfaces it implements, the class it extends—and others return information on the members of the class, including nested classes and interfaces. You can ask if a class represents an interface or an array, or whether a particular object is an instance of that class. We look at these different methods over the next few pages.

The most basic Class methods are those that walk the type hierarchy, displaying information about the interfaces implemented and the classes extended. As an example, this program prints the complete type hierarchy of the type represented by a string passed as an argument:

import java.lang.reflect.*;

public class TypeDesc {
    public static void main(String[] args) {
        TypeDesc desc = new TypeDesc();
        for (String name : args) {
            try {
                Class<?> startClass = Class.forName(name);
                desc.printType(startClass, 0, basic);
            } catch (ClassNotFoundException e) {
                System.err.println(e);  // report the error
        }
    }
}

// by default print on standard output
private java.io.PrintStream out = System.out;

// used in printType() for labeling type names
private static String[]
    basic   = { "class",   "interface",
                "enum",    "annotation"  },
    supercl = { "extends", "implements" },
    iFace   = { null,       "extends"  };

private void printType(
    Type type, int depth, String[] labels)
{
    if (type == null) // stop recursion -- no supertype
        return;

    // turn the Type into a Class object
    Class<?> cls = null;
    if (type instanceof Class<?>)
        cls = (Class<?>) type;
    else if (type instanceof ParameterizedType)
        cls = (Class<?>)
            ((ParameterizedType)type).getRawType();
    else
        throw new Error("Unexpected non-class type");

    // print this type
    for (int i = 0; i < depth; i++)
        out.print("  ");
    int kind = cls.isAnnotation() ? 3 :
        cls.isEnum() ? 2 :
        cls.isInterface() ? 1 : 0;
    out.print(labels[kind] + " ");
    out.print(cls.getCanonicalName());

    // print generic type parameters if present
    TypeVariable<?>[] params = cls.getTypeParameters();
        if (params.length > 0) {
            out.print('<'),
            for (TypeVariable<?> param : params) {
                out.print(param.getName());
                out.print(", ");
            }
            out.println(">");
        }
        else
            out.println();

        // print out all interfaces this class implements
        Type[] interfaces = cls.getGenericInterfaces();
        for (Type iface : interfaces) {
            printType(iface, depth + 1,
                      cls.isInterface() ? iFace : supercl);
        }
        // recurse on the superclass
        printType(cls.getGenericSuperclass(),
                  depth + 1, supercl);
    }
}

This program loops through the names provided on the command line, obtains the Class object for each named type and invokes printType on each of them. It must do this inside a try block in case there is no class of the specified name. Here is its output when invoked on the utility class java.util.HashMap:

class java.util.HashMap<K, V>
  implements java.util.Map<K, V>
  implements java.lang.Cloneable
  implements java.io.Serializable
  extends java.util.AbstractMap<K, V>
    implements java.util.Map<K, V>
    extends java.lang.Object

After the main method is the declaration of the output stream to use, by default System.out. The String arrays are described shortly.

The printType method prints its own type parameter's description and then invokes itself recursively to print the description of type's supertypes. Because we initially have general Type objects, we first have to convert them to Class objects. The depth parameter keeps track of how far up the type hierarchy it has climbed, indenting each description line according to its depth. The depth is incremented at each recursion level. The labels array specifies how to label the class—labels[0] is the label if the type is a class; labels[1] is for interfaces; labels[2] for enums; and labels[3] for annotation types. Note the order in which we check what kind of type we have—an enum type is a class, and an annotation type is an interface, so we have to check for these more specific kinds of type first.

Three arrays are defined for these labels: basic is used at the top level, supercl is used for superclasses, and iFace is used for superinterfaces of interfaces, which extend, not implement, each other. After we print the right prefix, we use getCanonicalName to print the full name of the type. The Class class provides a toString method, but it already adds “class” or “interface” in front. We want to control the prefix, so we must create our own implementation. We look more at class names a little later.

Next, the type is examined to see if it is a generic type. All classes implement GenericDeclaration, which defines the single method getTypeParameters that returns an array of TypeVariable objects. If we have one or more type parameters then we print each of their names between angle brackets, just as they would appear in your source code.

After printing the type description, printType invokes itself recursively, first on all the interfaces that the original type implements and then on the superclass this type extends (if any), passing the appropriate label array to each. Eventually, it reaches the Class object for Object, which implements no interfaces and whose getGenericSuperclass method returns null, and the recursion ends.

Some simple query methods, a few of which you saw in the example, examine the kind of Class object that you are dealing with:

  • public boolean isEnum()

    • Returns true if this Class object represents an enum.

  • public boolean isInterface()

    • Returns true if this Class object represents an interface (which includes annotation types).

  • public boolean isAnnotation()

    • Returns true if this Class object represents an annotation type.

  • public boolean isArray()

    • Returns true if this Class object represents an array.

  • public boolean isPrimitive()

    • Returns true if this Class object is one of the Class objects representing the eight primitive types or void.

  • public boolean isSynthetic()

    • Returns true if this Class object represents a synthetic type introduced by the compiler. A synthetic program element is anything for which there does not exist a corresponding construct in the source code.

  • public int getModifiers()

    • Returns the modifiers for the type, encoded as an integer. The value should be decoded using the constants and methods of class Modifier; see Section 16.3 on page 416. Type modifiers include the access modifiers (public, protected, private) as well as abstract, final, and static. For convenience, whether a type is an interface is also encoded as a modifier. Primitive types are always public and final. Array types are always final and have the same access modifier as their component type. In this context, an annotation is not considered a modifier because this encoding scheme pre-dates the addition of annotations to the language.

You can also ask whether you have a top-level type or a nested type, and if nested, some information about which kind of nested type:

  • public boolean isMemberClass()

    • Returns true if this Class object represents a type that is a member of another type—that is, it is a nested type. If this method returns false, then you have a top-level type. Despite the name, this method applies to nested interfaces, as well as nested classes.

  • public boolean isLocalClass()

    • Returns true if this class represents a local inner class.

  • public boolean isAnonymousClass()

    • Returns true if this class represents an anonymous inner class.

Note that there is no way to determine if a member class is a static nested class or an inner class.

The example showed the two methods you can use to find out where a type fits in the type hierarchy:

  • public Type[] getGenericInterfaces()

    • Returns an array of Type objects for each of the interfaces implemented by this type. If no interfaces are implemented you will get a zero-length array. If an implemented interface is a parameterized type, then the Type object will be an instance of ParameterizedType; otherwise, the Type object will be a Class object.

  • public Type getGenericSuperclass()

    • Returns the Type object for the superclass of this type. This returns null if this Class object represents the Object class, an interface, void, or primitive type, since these have no superclass. If this Class object represents an array, the Class object for Object is returned. By invoking this recursively you can determine all superclasses of a class. If the superclass is a parameterized type, the Type object will be an instance of ParameterizedType; otherwise, the Type object will be a Class object.

The legacy methods getInterfaces and getSuperclass are similar to the above except they return Class objects instead of Type objects. Any parameterized type is returned as the Class object for the corresponding raw type.

Given all the different kinds of types there are, some methods only apply to a subset of those different kinds:

  • public Class<?> getComponentType()

    • Returns the Class object representing the component type of the array represented by this Class object. If this Class object doesn't represent an array, null is returned. For example, given an array of int, the getClass method will return a Class object for which isArray returns true and whose getComponentType method returns the same object as int.class.

  • public T[] getEnumConstants()

    • Returns the elements of this enum class, or null if this Class object does not represent an enum.

  • public Class<?> getDeclaringClass()

    • Returns the Class object for the type in which this nested type was declared as a member. If this type is not a nested member type, null is returned. For example, when invoked on a local inner class or an anonymous inner class, this method returns null.

  • public Class<?> getEnclosingClass()

    • Returns the Class object for the enclosing type in which this type was declared. If this type is a top-level type, null is returned.

  • public Constructor<?> getEnclosingConstructor()

    • Returns the Constructor object (discussed shortly) for the constructor in which this local or anonymous inner class was declared. If this isn't a local or anonymous inner class declared in a constructor, then null is returned.

  • public Method getEnclosingMethod()

    • Returns the Method object (discussed shortly) for the method in which this local or anonymous inner class was declared. If this isn't a local or anonymous inner class declared in a method, then null is returned.

Exercise 16.1Modify TypeDesc to skip printing anything for the Object class. It is redundant because everything ultimately extends it. Use the reference for the Class object for the Object type.

Exercise 16.2Modify TypeDesc to show whether the named type is a nested type, and if so, what other type it is nested within.

Examining Class Members

The Class class contains a set of methods you can use to examine the class's components: its fields, methods, constructors, and nested types. Special types are defined to represent each of these, which you will learn about in detail in later sections: Field objects for fields, Method objects for methods, Constructor objects for constructors, and for nested types, the Class objects you have already seen.

These methods come in four variants depending on whether you want all members or a particular member, only public members or any members, only members declared in the current class or inherited members as well.

You can request all the public members of a specific kind that are either declared in the class (interface) or inherited, by using one of the following:[2]

public Constructor[] getConstructors()
public Field[] getFields()
public Method[] getMethods()
public Class[] getClasses()

Because constructors are not inherited, getConstructors returns Constructor objects only for public constructors declared in the current class.

You can also ask for the members of a specific kind that are actually declared in the class (interface), as opposed to being inherited. Such members need not be public:

public Constructor[] getDeclaredConstructors()
public Field[] getDeclaredFields()
public Method[] getDeclaredMethods()
public Class[] getDeclaredClasses()

In each case, if there is no member of that kind, you will get an empty array. The getClasses and getDeclaredClasses methods return both nested classes and nested interfaces.

Requesting a particular member requires you to supply further information. For nested types this is simply the name of the nested type and, in fact, there are no special methods to do this because Class.forName can be used for this purpose. Particular fields are requested using their name:

public Field getField(String name)
public Field getDeclaredField(String name)

The first form finds a public field that is either declared or inherited, while the second finds only a declared field that need not be public. If the named field does not exist, a NoSuchFieldException is thrown. Note that the implicit length field of an array is not returned when these methods are invoked on an array type.

Particular methods are identified by their signature—their name and a sequence of Class objects representing the number and type of their parameters. Remember that for a varargs method the last parameter will actually be an array.

public Method 
    getMethod(String name, Class... parameterTypes)
public Method 
    getDeclaredMethod(String name, Class... parameterTypes)

Similarly, constructors are identified by their parameter number and types:

public Constructor<T> 
     getConstructor(Class... parameterTypes)
public Constructor<T>
     getDeclaredConstructor(Class... parameterTypes)

In both cases, if the specified method or constructor does not exist, you will get a NoSuchMethodException.

Note that when you ask for a single constructor you get a parameterized constructor type, Constructor<T>, that matches the type of the Class object, while the array returned for a group of constructors is not parameterized by T. This is because you can't create a generic array. Any constructor extracted from the array is essentially of an unknown kind, but this only affects use of the invoke method, which we discuss a little later.

All the above methods require a security check before they can proceed and so will interact with any installed security manager—see “Security” on page 677. If no security manager is installed then all these methods will be allowed. Typically, security managers will allow any code to invoke methods that request information on the public members of a type—this enforces the normal language level access rules. However, access to non-public member information will usually be restricted to privileged code within the system. Note that the only distinction made is between public and non-public members—security managers do not have enough information to enforce protected or package-level access. If access is not allowed, a SecurityException is thrown.

The following program lists the public fields, methods, and constructors of a given class:

import java.lang.reflect.*;

  public class ClassContents {
     public static void main(String[] args) {
         try {
             Class<?> c = Class.forName(args[0]);
             System.out.println(c);
             printMembers(c.getFields());
             printMembers(c.getConstructors());
             printMembers(c.getMethods());
         } catch (ClassNotFoundException e) {
             System.out.println("unknown class: " + args[0]);
         }
     }

     private static void printMembers(Member[] mems) {
         for (Member m : mems) {
             if (m.getDeclaringClass() == Object.class)
                 continue;
             String decl = m.toString();
             System.out.print("    ");
             System.out.println(strip(decl, "java.lang."));
         }
     }

     // ... definition of strip ... 
}

We first get the Class object for the named class. We then get and print the arrays of member objects that represent all the public fields, constructors, and methods of the class. The printMembers method uses the Member object's toString method to get a string that describes the member, skipping members that are inherited from Object (using getDeclaringClass to see which class the member belongs to) since these are present in all classes and so are not very useful to repeat each time (strip removes any leading "java.lang." from the name). Here is the output when the program is run on the Attr class from page 76:

class Attr
    public Attr(String)
    public Attr(String, Object)
    public String Attr.toString()
    public String Attr.getName()
    public Object Attr.getValue()
    public Object Attr.setValue(Object)

Exercise 16.3Modify ClassContents to show information for all declared and all public inherited members. Make sure you don't list anything twice.

Naming Classes

The Class objects in the TypeDesc program are obtained using the static method Class.forName, which takes a string argument and returns a Class object for the type with that name. The name must be the binary name of a class or interface, or else be a specially named array type—these are defined below.

Every type is represented by a number of different names. The actual class or interface name is the simple name of the type. This is the shortest name you could write in your program to represent a given type. For example, the simple name of java.lang.Object is Object, the simple name of the nested Entry interface in the java.util.Map interface is Entry, the simple name of the type representing an array of Object instances is Object[], and the simple name of an array of int is int[]. All types except for anonymous inner classes have simple names. You can get the simple name of a type from the Class method getSimpleName.

The canonical name of a type is its full name, as you would write it in your programs, including, if applicable, the package and enclosing type in which the type was declared. For example, the canonical name of Object is java.lang.Object; for the nested Entry interface it is java.util.Map.Entry; for an array of Object instances it is java.lang.Object[]; and for an array of int is int[]. In more specific terms, the canonical name of a top-level class or interface is its package name followed by dot, followed by its simple name. The canonical name of a nested type is the canonical name of the type in which it is declared, followed by dot, followed by its simple name. The canonical name of an array type is the canonical name of its element type followed by []. Local inner classes and anonymous inner classes do not have canonical names. You can get the canonical name of a type from the Class method getCanonicalName.

The canonical name is an example of a fully qualified name—the simple name qualified by the package and enclosing type, if applicable. While simple names might be ambiguous in a given context, a fully qualified name uniquely determines a type. Each type has only one canonical name, but nested types (and so arrays of nested types) can have multiple fully qualified names. For example, the java.util.SortedMap interface extends java.util.Map and inherits the nested Entry interface. Consequently, you can identify the Entry type by the fully qualified name java.util.SortedMap.Entry.

The binary name of a class or interface (not array) is a name used to communicate with the virtual machine, such as by passing it to Class.forName to ask the virtual machine for a specific Class object. The binary name of a top-level class or interface is its canonical name. For nested types there is a special naming convention, as you learned in “Implementation of Nested Types” on page 149. Static nested types and inner classes (excluding local and anonymous inner classes) have a binary name that consists of the binary name of their immediately enclosing type, followed by $ and the simple name of the nested or inner type. For example, the binary name of the Entry interface is java.util.Map$Entry. For local inner classes, the binary name is the binary name of the enclosing class, followed by $, then a number, and then its simple name. For an anonymous inner class, the binary name is the binary name of the enclosing class, followed by $ and a number.[3] You get the binary name of a type from the Class method getName. This binary name is what forName expects as a class or interface name.

For their names, array types have a special notation, known as internal format because it is the format used inside the virtual machine. But for some reason this notation is not considered a binary name. This notation consists of a code representing the component type of the array, preceded by the character [. The component types are encoded as follows:

B

byte

C

char

D

double

F

float

I

int

J

long

Lclassname;

class or interface

S

short

Z

boolean

For example, an array of int is named [I, while an array of Object is named [Ljava.lang.Object; (note the trailing semicolon). A multidimensional array is just an array whose component type is an array, so, for example, a type declared as int[][] is named [[I—an array whose component type is named [I. These internal format names can be passed to forName to obtain the class objects for array types, and it is this name that getName returns for array types.

For primitive types and void, the simple name, canonical name, and binary name are all the same—the keyword that presents that type, such as int, float, or void—and all the name methods return the same name. For these types, Class objects cannot be obtained from forName. You must use the class literals, such as int.class, or the TYPE field of the appropriate wrapper class, such as Void.TYPE. If a name representing one of these types is used with forName, it is assumed to be a user-defined class or interface and is unlikely to be found.

Obtaining Class Objects by Name

The forName method we have been using is a simpler form of the more general forName method:

  • public static Class<?> forName(String name, boolean initialize, ClassLoader loader) throws ClassNotFoundException

    • Returns the Class object associated with the named class or interface, using the given class loader. Given the binary name for a class or interface (in the same format returned by getName) or an array name in internal format, this method attempts to locate, load, and link the class or interface. The specified class loader is used to load the class or interface. If loader is null, the class is loaded through the system class loader. The class is initialized only if initialize is true and it has not previously been initialized. For array types, the component type of the array is loaded, but not initialized.

As this method description indicates, obtaining the Class object for a class can involve loading, linking, and initializing the class—a fairly complex process that is described further in Section 16.13 on page 435. The simple Class.forName method uses the current class loader—the one that loaded the current class in which the forName invocation appears—and initializes the class if needed. If the class cannot be found then the checked exception ClassNotFoundException is thrown. The exception can contain a nested exception that describes the problem, which you can get from invoking getCause on the exception object. This will be either the nested exception or null. Because of the complexities of loading, linking, and initializing a class, these methods can also throw the unchecked exceptions LinkageError and ExceptionInInitializerError.

Runtime Type Queries

When writing your programs you can check the type of an object by using instanceof or change the type of an expression with a cast. In both cases you need to know the name of the type involved so you can write it in the code. When working with reflection, you have only Class objects to work with, so operators like instanceof and casts can't be used. The class Class provides several methods that provide functionality analogous to the language level operators:

  • public boolean isInstance(Object obj)

    • Determines if obj is assignment compatible with this class. This is the dynamic equivalent of the instanceof operator. It returns true if obj could be assigned to a variable of the type represented by this Class object; and false otherwise.

  • public T cast(Object obj)

    • Casts the object obj to the type represented by this Class object. If the cast fails then ClassCastException is thrown. This is needed in some rare circumstances when the type you want to cast to is represented by a type parameter and so you can't use an actual language-level cast.

  • public boolean isAssignableFrom(Class<?> type)

    • Returns true if this Class object represents a supertype of type or type itself; otherwise returns false.

Annotation Queries

You can ask about the annotations applied to a class or interface using methods that are similar to those used for asking about members, but which differ slightly in the details. These methods are all part of the AnnotatedElement interface. AnnotedElement is implemented by all the reflection classes that represent program elements: Class, Field, Method, Constructor, and Package. The discussion here applies to all these AnnotatedElement instances.

The annotation queries can only provide information on those annotations that are available at runtime—that is, those annotations with a retention policy of RetentionPolicy.RUNTIME (see “Retention Policies” on page 395).

You can ask for all the annotations present on an element, whether declared directly or inherited, by using getAnnotations, which returns an array of Annotation instances. Or you can ask for only the directly applied annotations using getDeclaredAnnotations, which also returns an array of Annotation instances. You can ask for a specific annotation using getAnnotation, which takes the type of the annotation as an argument and returns the Annotation object or null if it is not present. You can ask whether a specific annotation type is present on an element using the booleanisAnnotationPresent method. In contrast to the similarly named methods for querying members, there is no notion of public versus non-public annotations, and also no security checks.

The annotation instances that are returned are proxy objects that implement the interface defined by a given annotation type. The methods of the annotation type can be invoked to obtain the annotation values for each annotation element. For example, assuming our BugsFixed annotation type (see page 392) had a runtime retention policy, then given

@BugsFixed( { "457605", "532456"} )
class Foo { /* ... */ }

the code

Class<Foo> cls = Foo.class;
BugsFixed bugsFixed =
    (BugsFixed) cls.getAnnotation(BugsFixed.class);
String[] bugIds = bugsFixed.value();
for (String id : bugIds)
    System.out.println(id);

would print

457605
532456

If an annotation method represents an annotation element with a Class type, and that class can not be found at runtime, then you will get a TypeNotPresentException (which is an unchecked exception that is analogous to ClassNotFoundException).

Since the annotation type that is available at runtime could be different from the annotation type used to annotate the class being inspected, it is possible that the two uses of the type are incompatible. If this occurs, then trying to access an element of the annotation may throw an AnnotationTypeMismatchException or IncompleteAnnotationException. If an element type is an enum and the enum constant in the annotation is no longer present in the enum, then an EnumConstantNotPresentException is thrown.

Exercise 16.4Write a program that prints all the available annotations applied to a given type. (Only annotations with a retention policy of RUNTIME will be available.)

Exercise 16.5Expand ClassContents to include the available annotation information for each member.

The Modifier Class

The Modifier class defines int constants for all non-annotation modifiers: ABSTRACT, FINAL, INTERFACE, NATIVE, PRIVATE, PROTECTED, PUBLIC, STATIC, STRICT, SYNCHRONIZED, TRANSIENT, and VOLATILE. For each constant there is a corresponding query method isMod(intmodifiers) that returns true if modifier mod is present in the specified value. For example, if a field is declared

public static final int OAK = 0;

the value returned by its Field object's getModifiers method would be

Modifier.PUBLIC | Modifier.STATIC | Modifier.FINAL

The strictfp modifier is reflected by the constant STRICT. If code or a class is to be evaluated in strict floating point (see “Strict and Non-Strict Floating-Point Arithmetic” on page 203), the modifiers for the method, class, or interface will include the STRICT flag.

The query methods can be used to ask questions in a more symbolic fashion. For example, the expression

Modifier.isPrivate(field.getModifiers())

is equivalent to the more opaque expression

(field.getModifiers() & Modifier.PRIVATE) != 0

The Member classes

The classes Field, Constructor, and Method all implement the interface Member, which has four methods for properties that all members share:

  • Class getDeclaringClass()

    • Returns the Class object for the class in which this member is declared.

  • String getName()

    • Returns the name of this member.

  • int getModifiers()

    • Returns the language modifiers for the member encoded as an integer. This value should be decoded using the Modifier class.

  • boolean isSynthetic()

    • Returns true if this member is a synthetic member that was created by the compiler. For example, synthetic fields are often created in an inner class to hold a reference to the enclosing instance, and synthetic “bridge” methods are generated to support generic types—see Section A.3.1 on page 745.

Although a class or interface can be a member of another class, for historical reasons the class Class does not implement Member, although it supports methods of the same name and contract.

The toString method of all Member classes includes the complete declaration for the member, similar to how it would appear in your source code, including modifiers, type, and parameter types (where applicable). For historical reasons, toString does not include generic type information. The toGenericString method provides a more complete representation of the declaration of a member, including type parameters, and all use of parameterized types and type variables. For methods and constructors, the throws list is also included in the string.

Access Checking and AccessibleObject

The classes Field, Constructor, and Method are also all subclasses of the class AccessibleObject, which lets you enable or disable the checking of the language-level access modifiers, such as public and private. Normally, attempts to use reflection to access a member are subject to the same access checks that would be required for regular, explicit code—for example, if you cannot access a field directly in code, you cannot access it indirectly via reflection. You can disable this check by invoking setAccessible on the object with a value of true—the object is now accessible, regardless of language-level access control. This would be required, for example, if you were writing a debugger. The methods are:

  • public void setAccessible(boolean flag)

    • Sets the accessible flag for this object to the indicated boolean value. A value of true means that the object should suppress language-level access control (and so will always be accessible); false means the object should enforce language-level access control. If you are not allowed to change the accessibility of an object a SecurityException is thrown.

  • public static void setAccessible(AccessibleObject[] array, boolean flag)

    • A convenience method that sets the accessible flag for an array of objects. If setting the flag of an object throws a SecurityException only objects earlier in the array will have their flags set to the given value, and all other objects are unchanged.

  • public boolean isAccessible()

    • Returns the current value of the accessible flag for this object.

The Field Class

The Field class defines methods for asking the type of a field and for setting and getting the value of the field. Combined with the inherited Member methods, this allows you to find out everything about the field declaration and to manipulate the field of a specific object or class.

The getGenericType method returns the instance of Type that represents the field's declared type. For a plain type, such as String or int, this returns the associated Class object—String.class and int.class, respectively. For a parameterized type like List<String>, it will return a ParameterizedType instance. For a type variable like T, it will return a TypeVariable instance.

The getType legacy method returns the Class object for the type of the field. For plain types this acts the same as getGenericType. If the field's declared type is a parameterized type, then getType will return the class object for the erasure of the parameterized type—that is, the class object for the raw type. For example, for a field declared as List<String>, getType will return List.class. If the field's declared type is a type variable, then getType will return the class object for the erasure of the type variable. For example, given class Foo<T>, for a field declared with type T, getType would return Object.class. If Foo were declared as Foo<Textends Number>, then getType would return Number.class.

You can ask whether a field is an enum constant using isEnumConstant. You can also get and set the value of a field using the get and set methods. There is a general-purpose form of these methods that take Object arguments and return Object values, and more specific forms that deal directly with primitive types. All of these methods take an argument specifying which object to operate on. For static fields the object is ignored and can be null. The following method prints the value of a short field of an object:

public static void printShortField(Object o, String name)
    throws NoSuchFieldException, IllegalAccessException
{
    Field field = o.getClass().getField(name);
    short value = (Short) field.get(o);
    System.out.println(value);
}

The return value of get is whatever object the field references or, if the field is a primitive type, a wrapper object of the appropriate type. For our short field, get returns a Short object that contains the value of the field—that value is automatically unboxed for storing in the local variable value.

The set method can be used in a similar way. A method to set a short field to a provided value might look like this:

public static void
    setShortField(Object o, String name, short nv)
    throws NoSuchFieldException, IllegalAccessException
{
    Field field = o.getClass().getField(name);
    field.set(o, nv);
}

Although set takes an Object parameter, we can pass a short directly and let a boxing conversion wrap it in a Short object.

If the field of the specified object is not accessible and access control is being enforced, an IllegalAccessException is thrown. If the passed object does not have a type that declares the underlying field, an IllegalArgumentException is thrown. If the field is non-static and the passed object reference is null, a NullPointerException is thrown. Accessing a static field can require initializing a class, so it is also possible for an ExceptionInInitializerError to be thrown.

The Field class also has specific methods for getting and setting primitive types. You can invoke getPrimitiveType and setPrimitiveType on a Field object, where PrimitiveType is the primitive type name (with an initial uppercase letter). The get example just shown could have used the statement

short value = field.getShort(o);

The set example could have used

field.setShort(o, nv);

and avoided the use of the wrapper object.

Field implements AnnotatedElement, and the annotations on a field can be queried as discussed in Section 16.2 on page 414.

With some work you can use a Field object as a way to manipulate an arbitrary value, but you should avoid this when possible. The language is designed to catch as many programming errors as possible when the program is compiled. The less you write using indirections such as the Field object, the more your errors will be prevented before they are compiled into code. Also, as you can see, it takes more reading to see what is happening in the preceding code compared with what it would take if the name of the field were simply used in the normal syntax.

Final Fields

Under normal circumstances attempting to set the value of a field declared as final will result in IllegalAccessException being thrown. This is what you would expect: Final fields should never have their value changed. There are special circumstances—such as during custom deserialization (see page 554)—where it makes sense to change the value of a final field. You can do this via reflection only on instance fields, and only if setAccessible(true) has been invoked on the Field object. Note that it is not enough that setAccessible(true) would succeed, it must actually be called.

This capability is provided for highly specialized contexts and is not intended for general use—we mention it only for completeness. Changing a final field can have unexpected, possibly tragic consequences unless performed in specific contexts, such as custom deserialization. Outside those contexts, changes to a final field are not guaranteed to be visible. Even in such contexts, code using this technique must be guaranteed that security will not thwart its work. Changing a final field that is a constant variable (see page 46) will never result in the change being seen, except through the use of reflection—don't do it!

Exercise 16.6Create an Interpret program that creates an object of a requested type and allows the user to examine and modify fields of that object.

The Method Class

The Method class, together with its inherited Member methods allows you to obtain complete information about the declaration of a method:

  • public Type getGenericReturnType()

    • Returns the Type object for the type returned by this method. If the method is declared void the returned object is void.class.

  • public Type[] getGenericParameterTypes()

    • Returns an array of Type objects with the type of each parameter of this method, in the order in which the parameters are declared. If the method has no parameters an empty array is returned.

  • public Type[] getGenericExceptionTypes()

    • Returns an array of Type objects for each of the exception types listed in the throws clause for this method, in the order they are declared. If there are no declared exceptions an empty array is returned.

There are also the legacy methods getReturnType, getParameterTypes, and getExceptionTypes that return Class objects instead of Type objects. As with Field.getType, parameterized types and type variables are represented by the Class objects of their erasures.

Method implements AnnotatedElement, and the annotations on a method can be queried as discussed in Section 16.2 on page 414. Additionally, Method provides the getParameterAnnotations method to provide access to the annotations applied to the method's parameters. The getParameterAnnotations method return an array of Annotation arrays, with each element of the outermost array corresponding to the parameters of the method, in the order they are declared. If a parameter has no annotations then a zero-length Annotation array is provided for that parameter. If the Method object represents a method that is itself an element of an annotation, the getDefaultValue method returns an Object representing the default value of that element. If it is not an annotation element or if there is no default value, then null is returned.

The Method class also implements GenericDeclaration and so defines the method getTypeParameters which returns an array of TypeVariable objects. If a given Method object does not present a generic method, then an empty array is returned.

You can ask a Method object if it is a varargs (variable-argument) method using the isVarArgs method. The method isBridge asks if it is a bridge method (see Section A.3.1 on page 745).

The most interesting use of a Method object is to reflectively invoke it:

  • public Object invoke(Object onThis, Object... args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException

    • Invokes the method defined by this Method object on the object onThis, setting the parameters of the method from the values in args. For non-static methods the actual type of onThis determines the method implementation that is invoked. For static methods onThis is ignored and is traditionally null. The number of args values must equal the number of parameters for the method, and the types of those values must all be assignable to those of the method. Otherwise you will get an IllegalArgumentException. Note that for a varargs method the last parameter is an array that you must fill with the actual “variable” arguments you want to pass. If you attempt to invoke a method to which you do not have access, an IllegalAccessException is thrown. If this method is not a method of the onThis object, an IllegalArgumentException is thrown. If onThis is null and the method is not static, a NullPointerException is thrown. If this Method object represents a static method and the class has yet to be initialized, you might get an ExceptionInInitializerError. If the method throws an exception, an InvocationTargetException is thrown whose cause is that exception.

When you use invoke, you can either pass primitive types directly, or use wrappers of suitable types. The type represented by a wrapper must be assignable to the declared parameter type. You can use a Long, Float, or Double to wrap a double argument, but you cannot use a Double to wrap a long or float argument because a double is not assignable to a long or a float. The Object returned by invoke is handled as with Field.get, returning primitive types as their wrapper classes. If the method is declared void, invoke returns null.

Simply put, you can use invoke only to invoke a method with the same types and values that would be legal in the language. The invocation

return str.indexOf(".", 8);

can be written using reflection in the following way:

Throwable failure;
try {
    Method indexM = String.class.
        getMethod("indexOf", String.class, int.class);
    return (Integer) indexM.invoke(str, ".", 8);
} catch (NoSuchMethodException e) {
    failure = e;
} catch (InvocationTargetException e) {
    failure = e.getCause();
} catch (IllegalAccessException e) {
    failure = e;
}
throw failure;

The reflection-based code has semantically equivalent safety checks, although the checks that are made by the compiler for direct invocation can be made only at run time when you use invoke. The access checking may be done in a somewhat different way—you might be denied access to a method in your package by the security manager, even if you could invoke that method directly.

These are good reasons to avoid using this kind of invocation when you can. It's reasonable to use invoke—or the getset methods of Field—when you are writing a debugger or other generic applications that require interpreting user input as manipulations of objects. A Method object can be used somewhat like a method pointer in other languages, but there are better tools—notably interfaces, abstract classes, and nested classes—to address the problems typically solved by method pointers in those languages.

Exercise 16.7Modify your Interpret program to invoke methods on the object. You should properly display any values returned or exceptions thrown.

Creating New Objects and the Constructor Class

You can use a Class object's newInstance method to create a new instance (object) of the type it represents. This method invokes the class's no-arg constructor and returns a reference to the newly created object. For a class object of type Class<T> the returned object is of type T.

Creating a new object in this way is useful when you want to write general code and let the user specify the class. For example, you could modify the general sorting algorithm tester from “Designing a Class to Be Extended” on page 108 so that the user could type the name of the class to be tested and use that as a parameter to the forName lookup method. Assuming that the given class name was valid, newInstance could then be invoked to create an object of that type. Here is a new main method for a general TestSort class:

static double[] testData = { 0.3, 1.3e-2, 7.9, 3.17 };

public static void main(String[] args) {
    try {
        for (String name : args) {
            Class<?> classFor = Class.forName(name);
            SortDouble sorter
                = (SortDouble) classFor.newInstance();
            SortMetrics metrics
                = sorter.sort(testData);
            System.out.println(name + ": " + metrics);
            for (double data : testData)
                System.out.println("	" + data);
        }
    } catch (Exception e) {
        System.err.println(e);        // report the error
    }
}

This is almost exactly like TestSort.main (see page 112), but we have removed all type names. This main method can be used to test any subclass of SortDouble that provides a no-arg constructor. You don't have to write a main for each type of sorting algorithm—this generic main works for them all. All you need to do is execute

java TestSort TestClass ...

for any sorting class (such as SimpleSortDouble) and it will be loaded and run.

Note that whereas newInstance returns a T, Class.forName returns a Class<?>. This means that the actual kind of object returned by newInstance is unknown, so the cast is needed. You could instead get a class object of the exact type needed using asSubclass, and invoke newInstance without needing a cast:

Class<? extends SortDouble> classFor =
    Class.forName(name).asSubclass(SortDouble.class);
SortDouble sorter = classFor.newInstance();

In either case, if the named class is not a subtype of SortDouble, then a ClassCastException will be thrown.

The newInstance method can throw a number of different exceptions if used inappropriately. If the class doesn't have a no-arg constructor, is an abstract class, or interface, or if the creation fails for some other reason, you will get an InstantiationException. If the class or the no-arg constructor is not accessible an IllegalAccessException is thrown. If the current security policy disallows the creation of a new object a SecurityException is thrown. Finally, creating a new object may require the class to be initialized so it is also possible for an ExceptionInInitializerError to be thrown.

The newInstance method of Class invokes only a no-arg constructor. If you want to invoke any other constructor then you must use the Class object to get the relevant Constructor object and invoke newInstance on that Constructor, passing the appropriate parameters.

The Constructor class, together with its inherited Member methods allows you to obtain complete information about the declaration of a constructor and allows you to invoke the constructor to obtain a new instance of that class.

  • public Type[] getGenericParameterTypes()

    • Returns an array of Type objects for each of the parameter types accepted by this constructor, in the order in which the parameters are declared. If the constructor has no parameters an empty array is returned.

  • public Type[] getGenericExceptionTypes()

    • Returns an array of Type objects for each of the exception types listed in the throws clause for this constructor, in the order they are declared. If there are no declared exceptions an empty array is returned.

As with Method objects, the above are mirrored by the legacy versions getParameterTypes and getExceptionTypes, respectively.

The Constructor class is similar to Method, implementing the same interfaces (AnnotatedElement and GenericDeclaration) and defining similar methods (getParameterAnnotations and isVarArgs).

To create a new instance of a class from a Constructor object, you invoke its newInstance method.

  • public T newInstance(Object... args) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException

    • Uses the constructor represented by this Constructor object to create and initialize a new instance of the constructor's declaring class, with the specified initialization arguments. A reference to the newly initialized object is returned. Constructor.newInstance is very similar to Method.invoke. The number of args values must equal the number of parameters for the constructor, and the types of those values must all be assignable to those of the constructor. Otherwise you will get an IllegalArgumentException. Again, note that for a varargs constructor the last parameter is an array that you must fill in with the actual “variable” arguments you want to pass. If the declaring class is abstract you will get an InstantiationException. If the constructor is one to which you do not have access, you will get an IllegalAccessException. If the constructor itself throws an exception, you will get an InvocationTargetException whose cause is that exception.

If your constructor object is referenced through a wildcard reference, then you must cast the object returned by newInstance to the right type.

Exercise 16.8Modify your Interpret program further to let users invoke constructors of an arbitrary class, displaying any exceptions. If a construction is successful, let users invoke methods on the returned object.

Inner Class Constructors

An inner class (excluding local and anonymous inner classes) never has a no-arg constructor, since the compiler transforms all inner class constructors to take a first parameter that is a reference to the enclosing object. This means that you can never use Class.newInstance to create an inner class object, so you must use Constructor objects. The Constructor objects for an inner class reflect the transformed code, not the code written by the programmer. For example, consider the BankAccount class and its associated inner Action class from page 136. The Action class had a single constructor that took a String parameter, representing the action that had occurred (withdraw, deposit, and so on), and a long value, representing the amount involved. If you use getDeclaredConstructors to obtain that Constructor object and print its signature using toString, you will get the following:

BankAccount$Action(BankAccount,java.lang.String,long)

Here you can see both the use of the $ naming convention for inner classes and the implicitly added constructor argument that refers to the enclosing object. You can retrieve this constructor as follows:

Class<Action> actionClass = Action.class;
Constructor<Action> con =
    actionClass.getDeclaredConstructor(BankAccount.class,
            String.class, long.class);

If you want to construct an Action object you must supply an appropriate enclosing object reference as in

BankAccount acct = new BankAccount();
// ...
Action a = con.newInstance(acct, "Embezzle", 10000L);

Generic Type Inspection

As you saw in Figure 16-1 on page 400, there are a number of interfaces to represent the different kinds of types that can exist in your programs. So far we have focussed on Class objects and Member objects since they are the more commonly used reflection objects, and we have only mentioned the other kinds of Type objects in passing. This section looks at those other Type interfaces in more detail.

Type Variables

The GenericDeclaration interface, which is implemented by Class, Method, and Constructor, has the single method getTypeParameters, which returns an array of TypeVariable objects. The TypeVariable interface is itself a generic interface, declared as

interface TypeVariable<D extends GenericDeclaration>

So, for example, the TypeVariable objects Method.getTypeParameters returns would be of type TypeVariable<Method>.

Each type variable has a name returned by getName. It also has one or more upper bounds, obtained as a Type[] from getBounds. Recall that if there is no explicit upper bound then the upper bound is Object.

The getGenericDeclaration method returns the Type object for the GenericDeclaration in which the TypeVariable was declared. For example, the expression TypeVariable.class.getTypeParameters()[0] would yield a TypeVariable object that represents D in the declaration above. If you invoked getGenericDeclaration on this object, it would return the Class object for the TypeVariable interface. More generally, for any GenericDeclaration object g with at least one type parameter,

g.getTypeParameters()[i].getGenericDeclaration() == g

is always true (assuming i is a valid index of course).

Type variable objects are created on demand by the reflection methods that return them, and there is no requirement that you receive the same TypeVariable object each time you ask for the same type variable. However, the objects returned for a given type variable must be equivalent according to the equals method. The bounds for a type variable are not created until getBounds is invoked, so getBounds can throw TypeNotPresentException if a type used in the bounds cannot be found. It can also throw MalformedParameterizedTypeException if any of the bounds refers to a ParameterizedType instance that cannot be created for some reason.

Parameterized Types

Parameterized types, such as List<String>, are represented by objects that implement the ParameterizedType interface. You can obtain the actual type arguments for the parameterized type from getActualTypeArguments, which returns an array of Type objects. For example, getActualTypeArguments invoked on the parameterized type List<String> would yield an array of length one, containing String.class as its only element.

The getOwnerType method (which perhaps would have been better called getDeclaringType) returns the Type object for the type in which this ParameterizedType object is a member. If this is not a member of another type then null is returned. This is analogous to the Class.getDeclaringClass method except that a Type object is returned.

Like TypeVariable objects, ParameterizedType objects are created on demand and are not always the same object, so you should use equals not == to check for equivalent parameterized type objects. Also, when a parameterized type is created, all its type arguments are also created, and this applies recursively. Both of the above methods will sometimes throw either TypeNotFoundException or MalformedParameterizedTypeException.

Finally, ParameterizedType also has the method getRawType, which returns the Class object for the raw type of the parameterized type. For example, if getRawType were invoked on the parameterized type List<String> it would return List.class. Even though a raw type is by definition a non-generic class or interface, getRawType returns a Type instance rather than a Class<?>, so a cast must be applied, as you saw in the TypeDesc program.

Wildcards

A wildcard type parameter is represented by an instance that implements the WildcardType interface. For example, given a parameterized type for List<?extends Number>, getActualTypeArguments will give an array of length one containing a WildcardType object representing “?extends Number”.

WildcardType has two methods: getUpperBounds and getLowerBounds, which return Type arrays representing the upper and lower bounds of the wildcard, respectively. If no upper bound was specified, the upper bound is Object. If a wildcard has no lower bound, getLowerBounds returns an empty array.

As with TypeVariable, the type objects for bounds are created on demand, so TypeNotPresentException or MalformedParameterizedTypeException may be thrown.

Generic Arrays

The last of the type related interfaces is GenericArrayType. This represents array types in which the component type is a parameterized type or a type variable.[4] GenericArrayType has a single method, getGenericComponentType, which returns the Type for the component type of the array, which will be either a ParameterizedType or a TypeVariable. For example, for a List<String>[] field, getGenericType would return a GenericArrayType object whose getComponentType would return a ParameterizedType for List<String>.

The component type object gets created when getGenericComponentType is invoked, so as you might expect, you may get a TypeNotPresentException or MalformedParameterizedTypeException.

String Representations of Type Objects

None of the interfaces described above define the toString method, or any other general way to obtain a string representation of a type—with the exception of TypeVariable, which has the getName method. However, all the type objects will have a toString method defined. With no specification of what toString should return for Type objects, you cannot rely on toString to give a reasonable representation of the type. If you want a string representation of a type, you will need to assemble it yourself from the information available. For example, if a WildcardType object has no lower bound and an upper bound of X, then the wildcard is “?extends X”. For a ParameterizedType you can construct the string representation using the raw type and the actual type parameter types.

Exercise 16.9Use reflection to write a program that will print a full declaration of a named class, including everything except the import statements, comments, and code for initializers, constructors, and methods. The member declarations should appear just as you would write them. You will need to use all the reflection classes you have seen. Also note that the toString methods of many of the reflection objects will not provide the information you want in the correct format, so you will need to piece together the individual pieces of information.

Arrays

An array is an object but has no members—the implicit length “field” of an array is not an actual field. Asking the Class object of an array for fields, methods, or constructors will all yield empty arrays. To create arrays and to get and set the values of elements stored in an array, you can use the static methods of the Array class. You can create arrays with either of two newInstance methods.

  • public static Object newInstance(Class<?> componentType, int length)

    • Returns a reference to a new array of the specified length and with component type componentType.

  • public static Object newInstance(Class<?> componentType, int[] dimensions)

    • Returns a reference to a multidimensional array, with dimensions as specified by the elements of the dimensions array and with the component type componentType. If the dimensions array is empty or has a length greater than the number of dimensions allowed by the implementation (typically 255), an IllegalArgumentException is thrown.

For primitive types, use the class literal to obtain the Class object. For example, use byte.class to create a byte array. The statement

byte[] ba = (byte[]) Array.newInstance(byte.class, 13);

is equivalent to

byte[] ba = new byte[13];

The second newInstance method takes an array of dimensions. The statement

int[] dims = { 4, 4 };
double[][] matrix =
    (double[][]) Array.newInstance(double.class, dims);

is equivalent to

double[][] matrix = new double[4][4];

Because the component type could itself be an array type, the actual dimensions of the created array can be greater than that implied by the arguments to newInstance. For example, if intArray is a Class object for the type int[], then the invocation Array.newInstance(intArray,13) creates a two-dimensional array of type int[][]. When componentType is an array type, the component type of the created array is the component type of componentType. So, in the previous example, the resulting component type is int.

The static getLength method of Array returns the length of a given array.

The Array class also has static methods to get and set the individual elements of a specified array, similar to the get and set methods of class Field. The general get and set methods work with Objects. For example, given an int array, xa, the value xa[i] can be more laboriously and less clearly fetched as

Array.get(xa, i)

which returns an Integer object that must be unwrapped to extract the int value. You can set values in a similar way: xa[i]= 23 is the same as the more awkward

Array.set(xa, i, 23);

If the object passed as the array is not actually an array, you will get an IllegalArgumentException. If the value to be set is not assignable to the component type of the array (after unwrapping, if necessary), you will get an IllegalArgumentException.

The Array class also supports a full set of getType and setType methods for all the primitive types, as in

Array.setInt(xa, i, 23);

These avoid the need for intermediate wrapper objects.

Genericity and Dynamic Arrays

Recall the toArray method of the SingleLinkQueue class from Chapter 11. We promised back then that we'd show you how to create the array directly (instead of having it passed in) by having the type token for the actual type argument of the queue passed in. Here's a first attempt:

public E[] toArray_v1(Class<E> type) {
    int size = size();
    E[] arr = (E[]) Array.newInstance(type, size);
    int i = 0;
    for (Cell<E> c = head;
         c != null && i < size;
         c = c.getNext())
        arr[i++] = c.getElement();
    return arr;
}

This code works, but is less desirable than the generic version that took in the array to fill. The main problem is that the above causes an “unchecked” warning from the compiler. As you may have already noticed, the cast to E[] is a cast involving a type parameter and such casts have a different meaning at runtime—the actual cast will be to Object, which is the erasure of E. Despite this, it is apparent that the code above is type-safe: We ask for an array that has a component type of E and we try to use the returned object as such an array. The existence of the “unchecked” warning is a consequence of a limitation in the Array API and can't be avoided.

The second problem with the above is that it suffers from the same limitation as the original non-generic version of toArray—it will only allow an array of the exact element type to be created, not an array of any supertype. We can address this latter problem by turning the current version into a generic method, as we did previously:

public <T> T[] toArray(Class<T> type) {
    int size = size();
    T[] arr = (T[]) Array.newInstance(type, size);
    int i = 0;
    Object[] tmp = arr;
    for (Cell<E> c = head;
         c != null && i < size;
         c = c.getNext())
        tmp[i++] = c.getElement();
    return arr;
}

This version still has the “unchecked” warning—that can't be avoided—but it allows any Class object to passed in and tries to return an array with that component type. As with the generic version that takes the array as an argument, this version relies on the runtime checking of array stores to ensure that the component type passed is actually compatible with the element type of the current queue.

So you're left with two approaches for dealing with the toArray requirement: have the caller pass in the array and avoid warnings, but be forced to deal with an array of the wrong size, or have the caller pass in the type token for the element type and create an array of the right size, but be subjected to the “unchecked” warning. Or you could do as the collection classes do and combine both: Take in an array, but if it is the wrong size dynamically create another one, and get the warning. While we normally advise that you avoid “unchecked” warnings at all costs, the case of Array.newInstance is an exception.[5]

Exercise 16.10Modify Interpret further to allow users to specify a type and size of array to create; set and get the elements of that array; and access fields and invoke methods on specific elements of the array.

Packages

If you invoke the getPackage method on a Class object, you get a Package object that describes the package in which the class lives (the Package class is defined in java.lang). You can also get a Package object by invoking the static method getPackage with the name of the package, or you can use the static getPackages method which returns an array of all known packages in the system. The getName method returns the full name of the package.

Package objects are used differently than the other reflective types—you can't create or manipulate packages at run time. You use Package objects to obtain information about a package, such as its purpose, who created it, what version it is, and so on. We defer a discussion on this until we look at packages in detail in Chapter 18.

The Proxy Class

The Proxy class lets you create classes at runtime that implement one or more interfaces. This is an advanced, rarely needed feature, but when needed it is quite useful.

Suppose, for example, you want to log calls to an object so that when a failure happens you can print the last several methods invoked on the object. You could write such code by hand for a particular class, with a way to turn it on for a particular object. But that requires custom code for each type of object you want to monitor, as well as each object checking on each method invocation to see if calls should be logged.

You could instead write a general utility that used a Proxy-created class to log a call history. Objects created by that class would implement the relevant interfaces, interposing code that you provide between the caller's invocation of a method and the object's execution of it.

The Proxy model is that you invoke Proxy.getProxyClass with a class loader and an array of interfaces to get a Class object for the proxy. Proxy objects have one constructor, to which you pass an InvocationHandler object. You can get a Constructor object for this constructor from the Class object and use newInstance (passing in an invocation handler) to create a proxy object. The created object implements all the interfaces that were passed to getProxyClass, as well as the methods of Object. As a shortcut to get a proxy object, you can invoke Proxy.newProxyInstance, which takes a class loader, an array of interfaces, and an invocation handler. When methods are invoked on the proxy object, these method invocations are turned into calls to the invocation handler's invoke method.

Given all that, here is how a general debug logging class might look:

public class DebugProxy implements InvocationHandler {
    private final Object obj;           // underlying object
    private final List<Method> methods; // methods invoked
    private final List<Method> history; // viewable history

    private DebugProxy(Object obj) {
        this.obj = obj;
        methods = new ArrayList<Method>();
        history = Collections.unmodifiableList(methods);
    }

    public static synchronized Object proxyFor(Object obj) {
        Class<?> objClass = obj.getClass();
        return Proxy.newProxyInstance(
            objClass.getClassLoader(),
            objClass.getInterfaces(),
            new DebugProxy(obj));
    }
    public Object
        invoke(Object proxy, Method method, Object[] args)
        throws Throwable
    {
        methods.add(method); // log the call
        try {
            // invoke the real method
            return method.invoke(obj, args);
        } catch (InvocationTargetException e) {
            throw e.getCause();
        }
    }
    public List<Method> getHistory() { return history; }
}

If you want a debug proxy for a given object, you invoke proxyFor, as in

Object proxyObj = DebugProxy.proxyFor(realObj);

The object proxyObj would implement all the interfaces that realObj implements, plus the methods of Object. The object proxyObj is also associated with the instance of DebugProxy that was created—this instance is the invocation handler for the proxy. When a method is invoked on proxyObj, this leads to an invocation of the invoke method on the associated DebugProxy instance, passing proxyObj as the proxy object, a Method object representing the method that was invoked, and all the arguments to the method call. In our example invoke logs the invocation by adding it to its list of invoked methods and then invokes the method on the underlying realObj object, which was stored in the obj field.

The (read-only) history of method invocations on the proxy object can be retrieved from its DebugProxy instance. This is obtained by passing the proxy to the static Proxy class method getInvocationHandler:

DebugProxy h =
    (DebugProxy) Proxy.getInvocationHandler(proxyObj);
List<Method> history = h.getHistory();

If we hadn't used the newProxyInstance shortcut we would have needed to write the following in ProxyFor:

Class<?> objClass = obj.getClass();
Class<?> proxyClass = Proxy.getProxyClass(
    objClass.getClassLoader(),
    objClass.getInterfaces());
Constructor ctor = proxyClass.getConstructor(
    InvocationHandler.class);
return ctor.newInstance(new DebugProxy(obj));

The invocation handler's invoke method can throw Throwable. However, if invoke throws any exception that the original method could not throw, the invoker will get an UndeclaredThrowableException that returns the offending exception from its getCause method.

If you invoke getProxyClass twice with the same parameters (the same class loader and the same interfaces in the same order) you will get back the same Class object. If the interfaces are in another order or the class loader is different, you will get back different Class objects. The interface order matters because two interfaces in the list can potentially have methods with the same name and signature. If this happens, the Method object passed to invoke will have a declaring class of the first interface listed that declares that method (defined by a depth-first search of interfaces and superinterfaces).

The declaring class for the public non-final methods of Objectequals, hashCode, and toString—is always Object.class. The other methods of Object are not “proxied”; their methods are handled directly by the proxy object itself, not via a call to invoke. Most importantly, this means that a lock on a proxy object is just that—a lock on the proxy. Whatever object or objects the proxy uses to do its work (for example, in our case the underlying object whose methods are being traced) is not involved in the lock, including any uses of wait, notifyAll, or notify.

You can ask if a Class object represents a dynamically generated proxy class using the static Proxy.isProxyClass method.

Loading Classes

The runtime system loads classes when they are needed. Details of loading classes vary between implementations, but most of them use a class path mechanism to search for a class referenced by your code but not yet loaded into the runtime system. The class path is a list of places in which the system looks for class files. This default mechanism works well in many cases, but much of the power of the Java virtual machine is its ability to load classes from places that make sense to your application. To write an application that loads classes in ways different from the default mechanism, you must provide a ClassLoader object that can get the bytecodes for class implementations and load them into the runtime system.

For example, you might set up a game so that any player could write a class to play the game using whatever strategy the player chooses. The design would look something like this:

Loading Classes

To make this work, you would provide a Player abstract class that players would extend to implement their strategy. When players were ready to try their strategy, they would send the compiled class's bytecodes to your system. The bytecodes would need to be loaded into the game and the strategy evaluated by playing it against others. The score would then be returned to the player.

At the server, the game program loads each waiting Player class, creates an object of the new type, and runs its strategy against the game algorithm. When the results are known, they are reported to the player who submitted the strategy.

The communication mechanism isn't specified here, but it could be as simple as electronic mail, with players mailing their classes and receiving the results by return mail.

The interesting part is how the game program loads the compiled class files into its runtime system. This is the province of a class loader, which must extend the abstract ClassLoader class and override its findClass method:

  • protected Class<?> findClass(String name) throws ClassNotFoundException

    • Locates the bytecodes representing the class name and loads them into the virtual machine, returning the Class object created to represent that class.

In our example, we would provide a PlayerLoader class to read the bytecodes from the player classes and install each of them as a usable class. The basic loop would look like this:

public class Game {
    public static void main(String[] args) {
        String name;    // the class name
        while ((name = getNextPlayer()) != null) {
            try {
                PlayerLoader loader = new PlayerLoader();
                Class<? extends Player> classOf = 
                    loader.loadClass(name).
                        asSubclass(Player.class);
                Player player = classOf.newInstance();
                Game game = new Game();
                player.play(game);
                game.reportScore(name);
            } catch (Exception e) {
                reportException(name, e);
            }
        }
    }

    // ... definition of other methods ...
}

Each new game creates a new PlayerLoader object to load the class for that run of the game. The new loader loads the class, using loadClass, returning the Class object that represents it. That Class object is used to create a new object of the Player class. Then we create a new game and play it. When the game is finished, the score is reported. Without a new class loader for each run, attempting to load a class with the same name as one that had already been loaded would return the original class. This would prevent players from submitting updated versions of their classes with the same name.

You can obtain the class loader for a given Class object from its getClassLoader method. System classes need not have a class loader, so the method may return null.

Class loaders define namespaces that separate the classes within an application. If two classes have different class loaders then they are distinct classes, even if the binary data for the class was read from the same class file. Each distinct class maintains its own set of static variables and modifications to the static variables of one class have no effect on the other class.

Each thread has an associated ClassLoader that will be used by default to load classes. This context class loader can be specified at thread creation; if none is specified the parent thread's context class loader will be used. The context class loader of the first thread is typically the class loader used to load the application—the system class loader. The Thread methods getContextClassLoader and setContextClassLoader allow you to get and set the context class loader.

The ClassLoader Class

A class loader can delegate responsibility for loading a class to its parent class loader. The parent class loader can be specified as a constructor argument to ClassLoader:

  • protected ClassLoader()

    • Creates a class loader with an implicit parent class loader of the system class loader, as returned by getSystemClassLoader.

  • protected ClassLoader(ClassLoader parent)

    • Creates a class loader with the specified parent class loader.

A generic class loader, intended for use by others, should provide both forms of constructor to allow an explicit parent to be set.

The system class loader is typically the class loader used by the virtual machine to load the initial application class. You can get a reference to it from the static method getSystemClassLoader.

The bootstrap class loader is the class loader used to load those classes belonging to the virtual machine (classes like Object, String, List, and so forth). These classes are often referred to as system classes, a term which can lead to some confusion since the system classes are loaded by the bootstrap loader, whereas application classes are loaded by the system class loader. The bootstrap loader may or may not be represented by an actual ClassLoader object, so invoking getClassLoader on an instance of one of the system classes will typically return null, indicating it was loaded by the bootstrap loader.

You can get the parent class loader from the method getParent. If the class loader's parent is the bootstrap class loader, getParent may return null.

Class loaders are an integral part of the security architecture—see “Security” on page 677—so creating a class loader and asking for the parent class loader are checked operations that may throw a SecurityException.

The primary method of ClassLoader is loadClass:

  • public Class<?> loadClass(String name) throws ClassNotFoundException

    • Returns the Class object for the class with the specified binary name, loading the class if necessary. If the class cannot be loaded you will get a ClassNotFoundException.

The default implementation of loadClass, which is not usually overridden, attempts to load a class as follows:

  1. It checks to see if the class has already been loaded by invoking findLoadedClass. ClassLoader maintains a table of Class objects for all the classes loaded by the current class loader. If a class has been previously loaded findLoadedClass will return the existing Class object.

  2. If the class has not been loaded it invokes loadClass on the parent class loader. If the current class loader does not have a parent, the bootstrap class loader is used.

  3. If the class still has not been loaded, findClass is invoked to locate and load the class.

Note that the parent class loader is always given the chance to load the class first; only if the bootstrap loader and the system class loader fail to load a given class will your custom class loader be given the opportunity to do so. The implication is that your custom class loader must search for classes in places that are different from those searched by the system or bootstrap class loaders.

The PlayerLoader class extends ClassLoader to override findClass:

class PlayerLoader extends ClassLoader {
    public Class<?> findClass(String name)
        throws ClassNotFoundException
    {
        try {
            byte[] buf = bytesForClass(name);
            return defineClass(name, buf, 0, buf.length);
        } catch (IOException e) {
            throw new ClassNotFoundException(e.toString());
        }
    }

    // ... bytesForClass, and any other methods ...
}

The findClass method generally has to perform two actions. First, it has to locate the bytecodes for the specified class and load them into a byte array—the job of bytesForClass in our example. Second, it uses the utility method defineClass to actually load the class defined by those bytes into the virtual machine and return a Class object for that class.

  • protected final Class<?> defineClass(String name, byte[] data, int offset, int length) throws ClassFormatError

    • Returns a Class object for the named class whose binary representation is held in data. Only the bytes in data from offset to offset+length-1 are used to define the class. If the bytes in the subarray do not conform to a valid class file format, a ClassFormatError is thrown. The defineClass method is responsible for storing the Class object into the table searched by findLoadedClass.

An overloaded form of defineClass takes an additional ProtectionDomain argument, while the above form uses a default protection domain. Protection domains define the security permissions for objects in that domain—again see “Security” on page 677 for further details. Both forms of defineClass may throw SecurityException.

Before you can define a class you have to read the bytes for the class, and that is the purpose of bytesForClass:

protected byte[] bytesForClass(String name)
    throws IOException, ClassNotFoundException
{
    FileInputStream in = null;
    try {
        in = streamFor(name + ".class");
        int length = in.available(); // get byte count
        if (length == 0)
            throw new ClassNotFoundException(name);
        byte[] buf = new byte[length];
        in.read(buf);                // read the bytes
        return buf;
    } finally {
        if (in != null)
            in.close();
    }
}

This method uses streamFor (not shown) to get a FileInputStream to the class's bytecodes, assuming that the bytecodes are in a file named by the class name with a ".class" appended—streamFor knows where to search for the class files to be loaded. We then create a buffer for the bytes, read them all, and return the buffer.

When the class has been successfully loaded, findClass returns the new Class object returned by defineClass. There is no way to explicitly unload a class when it is no longer needed. You simply stop using it, allowing it to be garbage-collected when its ClassLoader is unreachable.

Exercise 16.11Expand on Game and Player to implement a simple game, such as tic-tac-toe. Score some Player implementations over several runs each.

Preparing a Class for Use

The class loader assists in only the first stage of making a class available. There are three steps in all:

  1. Loading—. Getting the bytecodes that implement the class and creating a Class object.

  2. Linking—. Verifying that the class's bytecodes conform to the language rules, preparing the virtual machine by allocating space for static fields, and (optionally) resolving all references in the class by, if necessary, loading the classes referred to.

  3. Initialization—. Initializing the superclass (if necessary, including loading and linking it), then executing all the static initializers and static initialization blocks of the class.

The Class object returned by defineClass only represents a loaded class—it has not yet been linked. You can explicitly link by invoking the (misnamed) resolveClass method:

  • protected final void resolveClass(Class<?> c)

    • Links the specified class if it has not already been linked.

The loadClass method we described does not resolve the class that is loaded. A protected, overloaded form of loadClass takes an additional boolean flag indicating whether or not to resolve the class before returning. The virtual machine will ensure that a class is resolved (linked) before it is initialized.

A class must be initialized immediately before the first occurrence of: an instance of the class being created; a static method of the class being invoked; or a non-final static field of the class being accessed. This includes the use of reflection methods to perform those actions. Additionally, before an assert statement is executed inside a nested type, the enclosing top-level class (if there is one) must be initialized. Using reflection to directly load a class may also trigger initialization—such as use of Class.forName(name)—but note that simple use of a class literal does not trigger initialization.

Loading Related Resources

Classes are the primary resources a program needs, but some classes need other associated resources, such as text, images, or sounds. Class loaders have ways to find class resources, and they can use the same mechanisms to get arbitrary resources stored with the class. In our game example, a particular playing strategy might have an associated “book” that tells it how to respond to particular situations. The following code, in a BoldPlayer class, would get an InputStream for such a book:

String book = "BoldPlayer.book";
InputStream in;
ClassLoader loader = this.getClass().getClassLoader();
if (loader != null)
    in = loader.getResourceAsStream(book);
else
    in = ClassLoader.getSystemResourceAsStream(book);

System resources are associated with system classes (the classes that may have no associated class loader instance). The static ClassLoader method getSystemResourceAsStream returns an InputStream for a named resource. The preceding code checks to see whether it has a class loader. If it does not, the class has been loaded by the bootstrap class loader; otherwise, it uses the class loader's getResourceAsStream method to turn its resource name into a byte input stream. The resource methods return null if the resource is not found.

The Class class provides a getResourceAsStream method to simplify getting resources from a class's class loader. The preceding code could be written more simply as

String book = "BoldPlayer.book";
InputStream in = BoldPlayer.class.getResourceAsStream(book);

Resource names must be made up of one or more valid identifiers separated by / characters, specifying a path to the resource. Typically, a class loader will search for resources in the same places it will search for class files.

Two other resource methods—getResource and getSystemResource—return URL objects that name the resources. The class java.net.URL is covered briefly on page 725; it provides methods to use uniform resource locators to access resources. You can invoke the getContents method on the URL objects returned by the class loader methods to get an object that represents the contents of that URL.

The getResources method returns a java.util.Enumeration object (an older variant of the Iterator you've seen before) that can step through URL objects for all the resources stored under a given name. The static method getSystemResources does the same for system resources.

The resource-getting methods first ask the parent class loader for the resource, or they ask the bootstrap class loader if there is no parent. If the resource cannot be found, then findResource or findResources is invoked. Just as loadClass is built on top of a findClass that you provide when you subclass ClassLoader, so the resource methods are built on top of two methods that you can override to find resources:

  • public URL findResource(String name)

    • Returns a URL object for the resource of the given name, or null if none was found. If there are multiple resources of the same name the implementation determines which should be returned. The default implementation in ClassLoader always returns null.

  • public Enumeration<URL> findResources(String name)

    • Returns an Enumeration of URL objects for all the resources of the given name. The default implementation in ClassLoader returns an enumeration through zero resources.

Here is a findResource for PlayerLoader:

public java.net.URL findResource(String name) {
    File f = fileFor(name);
    if (!f.exists())
        return null;
    try {
        return f.toURL();
    } catch (java.net.MalformedURLException e) {
        return null;        // shouldn't happen
    }
}

The fileFor method is analogous to the streamFor method of PlayerLoader. It returns a java.io.File object that corresponds to the named resource in the file system. If the named file actually exists, then the URL for the resource is created from the path. (The File class represents a pathname in the file system—see “The File Class” on page 543.)

The BoldPlayer class might come with a BoldPlayer.book that can be found in the same place as the class file. You can replace this version with your own by simply placing yours in a location where it will be found first by the system class loader or the bootstrap loader.

Exercise 16.12Modify your results for Exercise 16.11 to allow player strategies to use attached resources by implementing findResource and findResources.

Controlling Assertions at Runtime

In Chapter 12 you learned about assertions and how they can be turned on or off through command-line options that you pass to the virtual machine (see Section 12.10 on page 300). You can also affect assertion evaluation from running code, although this is rarely required. You will probably need to do this only if you are writing a harness for running other code and must provide your users with options to manage assertions. Such manipulation is done with methods on ClassLoader:

  • public void setDefaultAssertionStatus(boolean enabled)

    • Sets the default assertion status for all classes loaded in this loader. Child loaders are not affected, whether they exist at the time this method is invoked or after. The initial default assertion status for a class loader is false.

  • public void setPackageAssertionStatus(String packageName, boolean enabled)

    • Sets the default assertion status for all classes in the given package and it's subpackages that are loaded in this loader. A null package name means the unnamed package for this class loader—see Chapter 18, page 468.

  • public void setClassAssertionStatus(String className, boolean enabled)

    • Sets the default assertion status for the given class and for all its nested classes when loaded in this loader. You can only name top-level classes; nested classes are turned off and on via their enclosing class.

  • public void clearAssertionStatus()

    • Clears (turns off) all assertions for this class loader, wiping clear any previous settings, whether from method invocations or the command line.

In all cases the settings only apply to classes that are loaded and initialized in the future—you cannot change the assertion status of a class once it has been loaded. More specifically, the assertion status of a class is established during initialization: after initialization of the superclass, but before execution of any static initializers.

As with the command-line options, the class-specific declarations have precedence over the package-specific declarations, which in turn have precedence over the class loader's default status. In effect the command-line options simply request the virtual machine to invoke the appropriate method on the class loader. So the command-line options

-da:com.acme.Evaluator -ea:com.acme...

are equivalent to the calls

loader.setClassAssertionStatus("com.acme.Evaluator", false);
loader.setPackageAssertionStatus("com.acme", true);

where loader is the class loader that will be used. (Note that the methods do not use the ... syntax that the command line uses because it is apparent whether a name refers to a package or a class.)

 

Be and not seem.

 
 --Ralph Waldo Emerson


[1] It actually treats it as being Class<? extends S>, where S is the erasure of T. Parameterized types share the same Class object so the erasure is used to remove any parameterized types from the wildcard's bounds. For example, given List<String> l, then l.getClass() has the type Class<? extends List>.

[2] A number of methods return arrays of raw types, such as Class[] or Constructor[]. These methods should have been generified to return wildcard types: Class<?>[] or Constructor<?>[].

[3] Since the binary name is not uniquely specified for local and anonymous classes, you cannot use reflection to instantiate these classes in a portable way. Fortunately, it is exceedingly rare that you would want to.

[4] You may recall that you cannot create such an array, but you are permitted to declare variables of that type. The actual array creation can use only an unbounded wildcard type, such as new List<?>[1]. When you first assign the new array to a more specific variable, such as a List<String>[], you will get an “unchecked” warning because the compiler cannot guarantee that the current or future contents of the array will actually be List<String> objects. Such arrays are inherently unsafe and should be used with extreme caution—you should generally not create methods that return such arrays or take them as parameters.

[5] For compilers that support “unchecked” as a warning type, this situation is an ideal candidate for use of the @SuppressWarnings annotation that was mentioned on page 396,

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.75.217