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.
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
).
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.
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.1: Modify 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.2: Modify TypeDesc
to show whether the named type is a nested type, and if so, what other type it is nested within.
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.3: Modify ClassContents
to show information for all declared and all public inherited members. Make sure you don't list anything twice.
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:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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.
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
.
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
.
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.4: Write 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.5: Expand ClassContents
to include the available annotation information for each member.
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 is
Mod
(intmodifiers)
that returns true
if modifier mod
is present in the specified value. For example, if a field is declared
the value returned by its Field
object's getModifiers
method would be
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
is equivalent to the more opaque expression
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.
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 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 get
PrimitiveType
and set
PrimitiveType
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
The set example could have used
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.
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.6: Create 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, 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
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 get
–set
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.7: Modify your Interpret
program to invoke methods on the object. You should properly display any values returned or exceptions thrown.
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.8: Modify 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.
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);
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.
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.getTypeParameter
s 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, 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.
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.
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
.
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.9: Use 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.
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
is equivalent to
The second newInstance
method takes an array of dimensions. The statement
is equivalent to
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 Object
s. 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
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 get
Type
and set
Type
methods for all the primitive types, as in
These avoid the need for intermediate wrapper objects.
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.10: Modify 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.
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 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
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 Object
—equals
, 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.
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:
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.
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:
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.
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.
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.11: Expand on Game
and Player
to implement a simple game, such as tic-tac-toe. Score some Player
implementations over several runs each.
The class loader assists in only the first stage of making a class available. There are three steps in all:
Loading—. Getting the bytecodes that implement the class and creating a Class
object.
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.
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.
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
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.12: Modify your results for Exercise 16.11 to allow player strategies to use attached resources by implementing findResource
and findResources
.
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.