Download - Java Ref Election

Transcript
Page 1: Java Ref Election

Trail: The Reflection API

Uses of Reflection

Reflection is commonly used by programs which require the ability to examine or modify the runtime behavior of applications running in the Java virtual machine. This is a relatively advanced feature and should be used only by developers who have a strong grasp of the fundamentals of the language. With that caveat in mind, reflection is a powerful technique and can enable applications to perform operations which would otherwise be impossible.

Extensibility Features An application may make use of external, user-defined classes by creating instances of extensibility objects using their fully-qualified names. Class Browsers and Visual Development Environments A class browser needs to be able to enumerate the members of classes. Visual development environments can benefit from making use of type information available in reflection to aid the developer in writing correct code. Debuggers and Test Tools Debuggers need to be able to examine private members on classes. Test harnesses can make use of reflection to systematically call a discoverable set APIs defined on a class, to insure a high level of code coverage in a test suite.

Drawbacks of Reflection

Reflection is powerful, but should not be used indiscriminately. If it is possible to perform an operation without using reflection, then it is preferable to avoid using it. The following concerns should be kept in mind when accessing code via reflection.

Performance Overhead Because reflection involves types that are dynamically resolved, certain Java virtual machine optimizations can not be performed. Consequently, reflective operations have slower performance than their non-reflective counterparts, and should be avoided in sections of code which are called frequently in performance-sensitive applications. Security Restrictions Reflection requires a runtime permission which may not be present when running under a security manager. This is in an important consideration for code which has to run in a restricted security context, such as in an Applet. Exposure of Internals

1

Page 2: Java Ref Election

Since reflection allows code to perform operations that would be illegal in non-reflective code, such as accessing private fields and methods, the use of reflection can result in unexpected side-effects, which may render code dysfunctional and may destroy portability. Reflective code breaks abstractions and therefore may change behavior with upgrades of the platform.

Lesson: Classes

Every object is either a reference or primitive type. Reference types all inherit from java.lang.Object. Classes, enums, arrays, and interfaces are all reference types. There is a fixed set of primitive types: boolean, byte, short, int, long, char, float, and double. Examples of reference types include java.lang.String, all of the wrapper classes for primitive types such as java.lang.Double, the interface java.io.Serializable, and the enum javax.swing.SortOrder.

For every type of object, the Java virtual machine instantiates an immutable instance of java.lang.Class which provides methods to examine the runtime properties of the object including its members and type information. Class also provides the ability to create new classes and objects. Most importantly, it is the entry point for all of the Reflection APIs. This lesson covers the most commonly used reflection operations involving classes:

Retrieving Class Objects describes the ways to get a Class Examining Class Modifiers and Types shows how to access the

class declaration information Discovering Class Members illustrates how to list the constructors,

fields, methods, and nested classes in a class Troubleshooting describes common errors encountered when using

Class

Retrieving Class Objects

The entry point for all reflection operations is java.lang.Class. With the exception of java.lang.reflect.ReflectPermission, none of the classes in java.lang.reflect have public constructors. To get to these classes, it is necessary to invoke appropriate methods on Class. There are several ways to get a Class depending on whether the code has access to an object, the name of class, a type, or an existing Class.

Object.getClass()

2

Page 3: Java Ref Election

If an instance of an object is available, then the simplest way to get its Class is to invoke Object.getClass(). Of course, this only works for reference types which all inherit from Object. Some examples follow.

Class c = "foo".getClass();

Returns the Class for String

Class c = System.console().getClass();

There is a unique console associated with the virtual machine which is returned by the static method System.console(). The value returned by getClass() is the Class corresponding to java.io.Console.

enum E { A, B }Class c = A.getClass();

A is is an instance of the enum E; thus getClass() returns the Class corresponding to the enumeration type E.

byte[] bytes = new byte[1024];Class c = bytes.getClass();

Since arrays are Objects, it is also possible to invoke getClass() on an instance of an array. The returned Class corresponds to an array with component type byte.

import java.util.HashSet;import java.util.Set;

Set<String> s = new HashSet<String>();Class c = s.getClass();

In this case, java.util.Set is an interface to an object of type java.util.HashSet. The value returned by getClass() is the class corresponding to java.util.HashSet.

The .class Syntax

If the type is available but there is no instance then it is possible to obtain a Class by appending ".class" to the name of the type. This is also the easiest way to obtain the Class for a primitive type.

boolean b;Class c = b.getClass(); // compile-time error

Class c = boolean.class; // correct

3

Page 4: Java Ref Election

Note that the statement boolean.getClass() would produce a compile-time error because a boolean is a primitive type and cannot be dereferenced. The .class syntax returns the Class corresponding to the type boolean.

Class c = java.io.PrintStream.class;

The variable c will be the Class corresponding to the type java.io.PrintStream.

Class c = int[][][].class;

The .class syntax may be used to retrieve a Class corresponding to a multi-dimensional array of a given type.

Class.forName()

If the fully-qualified name of a class is available, it is possible to get the corresponding Class using the static method Class.forName(). This cannot be used for primitive types. The syntax for names of array classes is described by Class.getName(). This syntax is applicable to references and primitive types.

Class c = Class.forName("com.duke.MyLocaleServiceProvider");

This statement will create a class from the given fully-qualified name.

Class cDoubleArray = Class.forName("[D");

Class cStringArray = Class.forName("[[Ljava.lang.String;");

The variable cDoubleArray will contain the Class corresponding to an array of primitive type double (i.e. the same as double[].class). The cStringArray variable will contain the Class corresponding to a two-dimensional array of String (i.e. identical to String[][].class).

TYPE Field for Primitive Type Wrappers

The .class syntax is a more convenient and the preferred way to obtain the Class for a primitive type; however there is another way to acquire the Class. Each of the primitive types and void has a wrapper class in java.lang that is used for boxing of primitive types to reference types. Each wrapper class contains a field named TYPE which is equal to the Class for the primitive type being wrapped.

4

Page 5: Java Ref Election

Class c = Double.TYPE;

There is a class java.lang.Double which is used to wrap the primitive type double whenever an Object is required. The value of Double.TYPE is identical to that of double.class.

Class c = Void.TYPE;

Void.TYPE is identical to void.class.

Methods that Return Classes

There are several Reflection APIs which return classes but these may only be accessed if a Class has already been obtained either directly or indirectly.

Class.getSuperclass()

Returns the super class for the given class. Class c = javax.swing.JButton.class.getSuperclass();

The super class of javax.swing.JButton is javax.swing.AbstractButton. Class.getClasses()

Returns all the public classes, interfaces, and enums that are members of the class including inherited members.

Class<?>[] c = Character.class.getClasses();

Character contains two member classes Character.Subset and Character.UnicodeBlock. Class.getDeclaredClasses() Returns all of the classes interfaces, and enums that are explicitly declared in this class.

Class<?>[] c = Character.class.getDeclaredClasses();

Character contains two public member classes Character.Subset and Character.UnicodeBlock and one private class Character.CharacterCache. { Class, java.lang.reflect. { Field, Method, Constructor } }.getDeclaringClass() Returns the Class in which these members were declared. Anonymous classes will not have a declaring class but will have an enclosing class.

import java.lang.reflect.Field;

Field f = System.class.getField("out");Class c = f.getDeclaringClass();

The field out is declared in System. public class MyClass { static Object o = new Object() { public void m() {} };

5

Page 6: Java Ref Election

static Class<c> = o.getClass().getEnclosingClass();}

The declaring class of the anonymous class defined by o is null. Class.getEnclosingClass()

Returns the immediately enclosing class of the class. Class c = Thread.State.class().getEnclosingClass();

The enclosing class of the enum Thread.State is Thread. public class MyClass { static Object o = new Object() { public void m() {} }; static Class<c> = o.getClass().getEnclosingClass();}

The anonymous class defined by o is enclosed by MyClass.

Examining Class Modifiers and Types

A class may be declared with one or more modifiers which affect its runtime behavior:

Access modifiers: public, protected, and private Modifier requiring override: abstract Modifier restricting to one instance: static Modifier prohibiting value modification: final Modifier forcing strict floating point behavior: strictfp Annotations

Not all modifiers are allowed on all classes, for example an interface cannot be final and an enum cannot be abstract. java.lang.reflect.Modifier contains declarations for all possible modifiers. It also contains methods which may be used to decode the set of modifiers returned by Class.getModifiers().

The ClassDeclarationSpy example shows how to obtain the declaration components of a class including the modifiers, generic type parameters, implemented interfaces, and the inheritance path. Since Class implements the java.lang.reflect.AnnotatedElement interface it is also possible to query the runtime annotations.

import java.lang.annotation.Annotation;import java.lang.reflect.Modifier;import java.lang.reflect.Type;import java.lang.reflect.TypeVariable;import java.util.Arrays;import java.util.ArrayList;import java.util.List;import static java.lang.System.out;

6

Page 7: Java Ref Election

public class ClassDeclarationSpy { public static void main(String... args) {

try { Class<?> c = Class.forName(args[0]); out.format("Class:%n %s%n%n",

c.getCanonicalName()); out.format("Modifiers:%n %s%n%n",

Modifier.toString(c.getModifiers()));

out.format("Type Parameters:%n"); TypeVariable[] tv =

c.getTypeParameters(); if (tv.length != 0) {

out.format(" ");for (TypeVariable t : tv) out.format("%s ", t.getName());out.format("%n%n");

} else {out.format(" -- No Type Parameters

--%n%n"); }

out.format("Implemented Interfaces:%n"); Type[] intfs = c.getGenericInterfaces(); if (intfs.length != 0) {

for (Type intf : intfs) out.format(" %s%n",

intf.toString());out.format("%n");

} else {out.format(" -- No Implemented

Interfaces --%n%n"); }

out.format("Inheritance Path:%n"); List<Class> l = new ArrayList<Class>(); printAncestor(c, l); if (l.size() != 0) {

for (Class<?> cl : l) out.format(" %s%n",

cl.getCanonicalName());out.format("%n");

} else {out.format(" -- No Super Classes --

%n%n"); }

out.format("Annotations:%n"); Annotation[] ann = c.getAnnotations(); if (ann.length != 0) {

for (Annotation a : ann) out.format(" %s%n",

a.toString());out.format("%n");

} else {

7

Page 8: Java Ref Election

out.format(" -- No Annotations --%n%n");

}

// production code should handle this exception more gracefully

} catch (ClassNotFoundException x) { x.printStackTrace();}

}

private static void printAncestor(Class<?> c, List<Class> l) {

Class<?> ancestor = c.getSuperclass(); if (ancestor != null) {

l.add(ancestor); printAncestor(ancestor, l);

} }}

A few samples of the output follows. User input is in italics.

$ java ClassDeclarationSpy java.util.concurrent.ConcurrentNavigableMapClass: java.util.concurrent.ConcurrentNavigableMap

Modifiers: public abstract interface

Type Parameters: K V

Implemented Interfaces: java.util.concurrent.ConcurrentMap<K, V> java.util.NavigableMap<K, V>

Inheritance Path: -- No Super Classes --

Annotations: -- No Annotations --

This is the actual declaration for java.util.concurrent.ConcurrentNavigableMap in the source code:

public interface ConcurrentNavigableMap<K,V> extends ConcurrentMap, NavigableMap<K,V>

Note that since this is an interface, it is implicitly abstract. The compiler adds this modifier for every interface. Also, this declaration contains two generic type parameters, K and V. The example code simply prints the names of these parameters, but is it possible to retrieve additional

8

Page 9: Java Ref Election

information about them using methods in java.lang.reflect.TypeVariable. Interfaces may also implement other interfaces as shown above.

$ java ClassDeclarationSpy "[Ljava.lang.String;"Class: java.lang.String[]

Modifiers: public abstract final

Type Parameters: -- No Type Parameters --

Implemented Interfaces: interface java.lang.Cloneable interface java.io.Serializable

Inheritance Path: java.lang.Object

Annotations: -- No Annotations --

Since arrays are runtime objects, all of the type information is defined by the Java virtual machine. In particular, arrays implement Cloneable and java.io.Serializable and their direct superclass is always Object.

$ java ClassDeclarationSpy java.io.InterruptedIOExceptionClass: java.io.InterruptedIOException

Modifiers: public

Type Parameters: -- No Type Parameters --

Implemented Interfaces: -- No Implemented Interfaces --

Inheritance Path: java.io.IOException java.lang.Exception java.lang.Throwable java.lang.Object

Annotations: -- No Annotations --

From the inheritance path, it may be deduced that java.io.InterruptedIOException is a checked exception because RuntimeException is not present.

9

Page 10: Java Ref Election

$ java ClassDeclarationSpy java.security.IdentityClass: java.security.Identity

Modifiers: public abstract

Type Parameters: -- No Type Parameters --

Implemented Interfaces: interface java.security.Principal interface java.io.Serializable

Inheritance Path: java.lang.Object

Annotations: @java.lang.Deprecated()

This output shows that java.security.Identity, a deprecated API, possesses the annotation java.lang.Deprecated. This may be used by reflective code to detect deprecated APIs.

Note: Not all annotations are available via reflection. Only those which have a java.lang.annotation.RetentionPolicy of RUNTIME are accessible. Of the three annotations pre-defined in the language @Deprecated, @Override, and @SuppressWarnings only @Deprecated is available at runtime.

Discovering Class Members

There are two categories of methods provided in Class for accessing fields, methods, and constructors: methods which enumerate these members and methods which search for particular members. Also there are distinct methods for accessing members declared directly on the class versus methods which search the superinterfaces and superclasses for inherited members. The following table provides a summary of all the member-locating methods and their characteristics.

Class Methods for Locating Members

Member Class API List of

members? Inherited members?

Private members?

10

Page 11: Java Ref Election

Field

getDeclaredField() no no yes

getField() no yes no

getDeclaredFields() yes no yes

getFields() yes yes no

Method

getDeclaredMethod() no no yes

getMethod() no yes no

getDeclaredMethods() yes no yes

getMethods() yes yes no

Constructor

getDeclaredConstructor() no N/A1 yes

getConstructor() no N/A1 no

getDeclaredConstructors() yes N/A1 yes

getConstructors() yes N/A1 no

1 Constructors are not inherited.

Given a class name and an indication of which members are of interest, the ClassSpy example uses the get*s() methods to determine the list of all public elements, including any which are inherited.

import java.lang.reflect.Constructor;import java.lang.reflect.Field;import java.lang.reflect.Method;import java.lang.reflect.Member;import static java.lang.System.out;

enum ClassMember { CONSTRUCTOR, FIELD, METHOD, CLASS, ALL }

public class ClassSpy { public static void main(String... args) {

try { Class<?> c = Class.forName(args[0]); out.format("Class:%n %s%n%n",

c.getCanonicalName());

Package p = c.getPackage(); out.format("Package:%n %s%n%n",

(p != null ? p.getName() : "-- No Package --"));

for (int i = 1; i < args.length; i++) {switch (ClassMember.valueOf(args[i]))

{case CONSTRUCTOR: printMembers(c.getConstructors(),

"Constructor");

11

Page 12: Java Ref Election

break;case FIELD: printMembers(c.getFields(),

"Fields"); break;case METHOD: printMembers(c.getMethods(),

"Methods"); break;case CLASS: printClasses(c); break;case ALL: printMembers(c.getConstructors(),

"Constuctors"); printMembers(c.getFields(),

"Fields"); printMembers(c.getMethods(),

"Methods"); printClasses(c); break;default: assert false;}

}

// production code should handle these exceptions more gracefully

} catch (ClassNotFoundException x) { x.printStackTrace();}

}

private static void printMembers(Member[] mbrs, String s) {

out.format("%s:%n", s);for (Member mbr : mbrs) { if (mbr instanceof Field)

out.format(" %s%n", ((Field)mbr).toGenericString());

else if (mbr instanceof Constructor)out.format(" %s%n",

((Constructor)mbr).toGenericString()); else if (mbr instanceof Method)

out.format(" %s%n", ((Method)mbr).toGenericString());

}if (mbrs.length == 0) out.format(" -- No %s --%n", s);out.format("%n");

}

private static void printClasses(Class<?> c) {

out.format("Classes:%n");Class<?>[] clss = c.getClasses();for (Class<?> cls : clss)

12

Page 13: Java Ref Election

out.format(" %s%n", cls.getCanonicalName());

if (clss.length == 0) out.format(" -- No member interfaces,

classes, or enums --%n");out.format("%n");

}}

This example is relatively compact; however the printMembers() method is slightly awkward due to the fact that the java.lang.reflect.Member interface has existed since the earliest implementations of reflection and it could not be modified to include the more useful getGenericString() method when generics were introduced. The only alternatives are to test and cast as shown, replace this method with printConstructors(), printFields(), and printMethods(), or to be satisfied with the relatively spare results of Member.getName().

Samples of the output and their interpretation follows. User input is in italics.

$ java ClassSpy java.lang.ClassCastException CONSTRUCTORClass: java.lang.ClassCastException

Package: java.lang

Constructor: public java.lang.ClassCastException() public java.lang.ClassCastException(java.lang.String)

Since constructors are not inherited, the exception chaining mechanism constructors (those with a Throwable parameter) which are defined in the immediate super class RuntimeException and other super classes are not found.

$ java ClassSpy java.nio.channels.ReadableByteChannel METHODClass: java.nio.channels.ReadableByteChannel

Package: java.nio.channels

Methods: public abstract int java.nio.channels.ReadableByteChannel.read(java.nio.ByteBuffer) throws java.io.IOException

13

Page 14: Java Ref Election

public abstract void java.nio.channels.Channel.close() throws java.io.IOException public abstract boolean java.nio.channels.Channel.isOpen()

The interface java.nio.channels.ReadableByteChannel defines read(). The remaining methods are inherited from a super interface. This code could easily be modified to list only those methods that are actually declared in the class by replacing get*s() with getDeclared*s().

$ java ClassSpy ClassMember FIELD METHODClass: ClassMember

Package: -- No Package --

Fields: public static final ClassMember ClassMember.CONSTRUCTOR public static final ClassMember ClassMember.FIELD public static final ClassMember ClassMember.METHOD public static final ClassMember ClassMember.CLASS public static final ClassMember ClassMember.ALL

Methods: public static ClassMember ClassMember.valueOf(java.lang.String) public static ClassMember[] ClassMember.values() public final int java.lang.Enum.hashCode() public final int java.lang.Enum.compareTo(E) public int java.lang.Enum.compareTo(java.lang.Object) public final java.lang.String java.lang.Enum.name() public final boolean java.lang.Enum.equals(java.lang.Object) public java.lang.String java.lang.Enum.toString() public static <T> T java.lang.Enum.valueOf(java.lang.Class<T>,java.lang.String) public final java.lang.Class<E> java.lang.Enum.getDeclaringClass() public final int java.lang.Enum.ordinal() public final native java.lang.Class<?> java.lang.Object.getClass()

14

Page 15: Java Ref Election

public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException public final void java.lang.Object.wait() throws java.lang.InterruptedException public final native void java.lang.Object.notify() public final native void java.lang.Object.notifyAll()

In the fields portion of these results, enum constants are listed. While these are technically fields, it might be useful to distinguish them from other fields. This example could be modified to use java.lang.reflect.Field.isEnumConstant() for this purpose. The EnumSpy example in a later section of this trail, Examining Enums, contains a possible implementation.

In the methods section of the output, observe that the method name includes the name of the declaring class. Thus, the toString() method is implemented by Enum, not inherited from Object. The code could be amended to make this more obvious by using Field.getDeclaringClass(). The following fragment illustrates part of a potential solution.

if (mbr instanceof Field) { Field f = (Field)mbr; out.format(" %s%n", f.toGenericString()); out.format(" -- declared in: %s%n", f.getDeclaringClass());}

Troubleshooting

The following examples show typical errors which may be encountered when reflecting on classes.

Compiler Warning: "Note: ... uses unchecked or unsafe operations"

When a method is invoked, the types of the argument values are checked and possibly converted. ClassWarning invokes getMethod() to cause a typical unchecked conversion warning:

import java.lang.reflect.Method;

15

Page 16: Java Ref Election

public class ClassWarning { void m() {

try { Class c = ClassWarning.class; Method m = c.getMethod("m"); // warning

// production code should handle this exception more gracefully

} catch (NoSuchMethodException x) { x.printStackTrace(); } }}$ javac ClassWarning.javaNote: ClassWarning.java uses unchecked or unsafe operations.Note: Recompile with -Xlint:unchecked for details.$ javac -Xlint:unchecked ClassWarning.javaClassWarning.java:7: warning: [unchecked] unchecked call to getMethod(java.lang.String,java.lang.Class<?>...) as a member of the raw type java.lang.Class Method m = c.getMethod("m"); // warning ^1 warning

Many library methods have been retrofitted with generic declarations including several in Class. Since c is declared as a raw type (has no type parameters) and the corresponding parameter of getMethod() is a parameterized type, an unchecked conversion occurs. The compiler is required to generate a warning. (See The Java Language Specification, Third Edition, sections 5.1.9 and 5.3.)

There are two possible solutions. The more preferable it to modify the declaration of c to include an appropriate generic type. In this case, the declaration should be:

Class<?> c = warn.getClass();

Alternatively, the warning could be explicitly suppressed using the predefined annotation @SuppressWarnings preceding the problematic statement.

Class c = ClassWarning.class;@SuppressWarnings("unchecked")Method m = c.getMethod("m"); // warning gone

16

Page 17: Java Ref Election

Tip: As a general principle, warnings should not be ignored as they may indicate the presence of a bug. Parameterized declarations should be used as appropriate. If that is not possible (perhaps because an application must interact with a library vendor's code), annotate the offending line using @SuppressWarnings.

InstantiationException when the Constructor is Not Accessible

Class.newInstance() will throw an InstantiationException if an attempt is made to create a new instance of the class and the zero-argument constructor is not visible. The ClassTrouble example illustrates the resulting stack trace.

class Cls { private Cls() {}}

public class ClassTrouble { public static void main(String... args) {

try { Class<?> c = Class.forName("Cls"); c.newInstance(); //

InstantiationException

// production code should handle these exceptions more gracefully

} catch (InstantiationException x) { x.printStackTrace();} catch (IllegalAccessException x) { x.printStackTrace();} catch (ClassNotFoundException x) { x.printStackTrace();}

}}$ java ClassTroublejava.lang.IllegalAccessException: Class ClassTrouble can not access a member ofclass Cls with modifiers "private" at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:65) at java.lang.Class.newInstance0(Class.java:349) at java.lang.Class.newInstance(Class.java:308)

17

Page 18: Java Ref Election

at ClassTrouble.main(ClassTrouble.java:9)

Class.newInstance() behaves very much like the new keyword and will fail for the same reasons new would fail. The typical solution in reflection is to take advantage of the java.lang.reflect.AccessibleObject class which provides the ability to suppress access control checks; however, this approach will not work because java.lang.Class does not extend AccessibleObject. The only solution is to modify the code to use Constructor.newInstance() which does extend AccessibleObject.

Tip: In general, it is preferable to use Constructor.newInstance() for the reasons described in the Creating New Class Instances section in the Members lesson.

Additional examples of potential problems using Constructor.newInstance() may be found in the Constructor Troubleshooting section of the Members lesson.

Fields

A field is a class, interface, or enum with an associated value. Methods in the java.lang.reflect.Field class can retrieve information about the field, such as its name, type, modifiers, and annotations. (The section Examining Class Modifiers and Types in the Classes lesson describes how to retrieve annotations.) There are also methods which enable dynamic access and modification of the value of the field. These tasks are covered in the following sections:

Obtaining Field Types describes how to get the declared and generic types of a field

Retrieving and Parsing Field Modifiers shows how to get portions of the field declaration such as public or transient

Getting and Setting Field Values illustrates how to access field values

Troubleshooting describes some common coding errors which may cause confusion

When writing an application such as a class browser, it might be useful to find out which fields belong to a particular class. A class's fields are

18

Page 19: Java Ref Election

identified by invoking Class.getFields(). The getFields() method returns an array of Field objects containing one object per accessible public field.

A public field is accessible if it is a member of either:

this class a superclass of this class an interface implemented by this class an interface extended from an interface implemented by this class

A field may be a class (instance) field, such as java.io.Reader.lock , a static field, such as java.lang.Integer.MAX_VALUE , or an enum constant, such as java.lang.Thread.State.WAITING.

Obtaining Field Types

A field may be either of primitive or reference type. There are eight primitive types: boolean, byte, short, int, long, char, float, and double. A reference type is anything that is a direct or indirect subclass of java.lang.Object including interfaces, arrays, and enumerated types.

The FieldSpy example prints the field's type and generic type given a fully-qualified binary class name and field name.

import java.lang.reflect.Field;import java.util.List;

public class FieldSpy<T> { public boolean[][] b = {{ false, false }, { true, true } }; public String name = "Alice"; public List<Integer> list; public T val;

public static void main(String... args) {try { Class<?> c = Class.forName(args[0]); Field f = c.getField(args[1]); System.out.format("Type: %s%n",

f.getType()); System.out.format("GenericType: %s%n",

f.getGenericType());

// production code should handle these exceptions more gracefully

} catch (ClassNotFoundException x) { x.printStackTrace();

19

Page 20: Java Ref Election

} catch (NoSuchFieldException x) { x.printStackTrace();}

}}

Sample output to retrieve the type of the three public fields in this class (b, name, and the parameterized type list), follows. User input is in italics.

$ java FieldSpy FieldSpy bType: class [[ZGenericType: class [[Z$ java FieldSpy FieldSpy nameType: class java.lang.StringGenericType: class java.lang.String$ java FieldSpy FieldSpy listType: interface java.util.ListGenericType: java.util.List<java.lang.Integer>$ java FieldSpy FieldSpy valType: class java.lang.ObjectGenericType: T

The type for the field b is two-dimensional array of boolean. The syntax for the type name is described in Class.getName().

The type for the field val is reported as java.lang.Object because generics are implemented via type erasure which removes all information regarding generic types during compilation. Thus T is replaced by the upper bound of the type variable, in this case, java.lang.Object.

Field.getGenericType() will consult the Signature Attribute in the class file if it's present. If the attribute isn't available, it falls back on Field.getType() which was not changed by the introduction of generics. The other methods in reflection with name getGenericFoo for some value of Foo are implemented similarly.

Retrieving and Parsing Field Modifiers

There are several modifiers that may be part of a field declaration:

Access modifiers: public, protected, and private Field-specific modifiers governing runtime behavior: transient

and volatile Modifier restricting to one instance: static Modifier prohibiting value modification: final Annotations

20

Page 21: Java Ref Election

The method Field.getModifiers() can be used to return the integer representing the set of declared modifiers for the field. The bits representing the modifiers in this integer are defined in java.lang.reflect.Modifier.

The FieldModifierSpy example illustrates how to search for fields with a given modifier. It also determines whether the located field is synthetic (compiler-generated) or is an enum constant by invoking Field.isSynthetic() and Field.isEnumCostant() respectively.

import java.lang.reflect.Field;import java.lang.reflect.Modifier;import static java.lang.System.out;

enum Spy { BLACK , WHITE }

public class FieldModifierSpy { volatile int share; int instance; class Inner {}

public static void main(String... args) {try { Class<?> c = Class.forName(args[0]); int searchMods = 0x0; for (int i = 1; i < args.length; i++) {

searchMods |= modifierFromString(args[i]);

}

Field[] flds = c.getDeclaredFields(); out.format("Fields in Class '%s'

containing modifiers: %s%n", c.getName(),

Modifier.toString(searchMods)); boolean found = false; for (Field f : flds) {

int foundMods = f.getModifiers();// Require all of the requested

modifiers to be presentif ((foundMods & searchMods) ==

searchMods) { out.format("%-8s [ synthetic=%-5b

enum_constant=%-5b ]%n", f.getName(),

f.isSynthetic(), f.isEnumConstant());

found = true;}

}

if (!found) {

21

Page 22: Java Ref Election

out.format("No matching fields%n"); }

// production code should handle this exception more gracefully

} catch (ClassNotFoundException x) { x.printStackTrace();}

}

private static int modifierFromString(String s) {

int m = 0x0;if ("public".equals(s)) m |=

Modifier.PUBLIC;else if ("protected".equals(s)) m |=

Modifier.PROTECTED;else if ("private".equals(s)) m |=

Modifier.PRIVATE;else if ("static".equals(s)) m |=

Modifier.STATIC;else if ("final".equals(s)) m |=

Modifier.FINAL;else if ("transient".equals(s)) m |=

Modifier.TRANSIENT;else if ("volatile".equals(s)) m |=

Modifier.VOLATILE;return m;

}}

Sample output follows:

$ java FieldModifierSpy FieldModifierSpy volatileFields in Class 'FieldModifierSpy' containing modifiers: volatileshare [ synthetic=false enum_constant=false ]$ java FieldModifierSpy Spy publicFields in Class 'Spy' containing modifiers: publicBLACK [ synthetic=false enum_constant=true ]WHITE [ synthetic=false enum_constant=true ]$ java FieldModifierSpy FieldModifierSpy\$Inner finalFields in Class 'FieldModifierSpy$Inner' containing modifiers: finalthis$0 [ synthetic=true enum_constant=false ]$ java FieldModifierSpy Spy private static finalFields in Class 'Spy' containing modifiers: private static final$VALUES [ synthetic=true enum_constant=false ]

Notice that some fields are reported even though they are not declared in the original code. This is because the compiler will generate some

22

Page 23: Java Ref Election

synthetic fields which are needed during runtime. To test whether a field is synthetic, the example invokes Field.isSynthetic(). The set of synthetic fields is compiler-dependent; however commonly used fields include this$0 for inner classes (i.e. nested classes that are not static member classes) to reference the outermost enclosing class and $VALUES used by enums to implement the implicitly defined static method values(). The names of synthetic class members are not specified and may not be the same in all compiler implementations or releases. These and other synthetic fields will be included in the array returned by Class.getDeclaredFields() but not identified by Class.getField() since synthetic members are not typically public.

Because Field implements the interface java.lang.reflect.AnnotatedElement, it is possible to retrieve any runtime annotation with java.lang.annotation.RetentionPolicy.RUNTIME. For an example of obtaining annotations see the section Examining Class Modifiers and Types

Getting and Setting Field Values

Given an instance of a class, it is possible to use reflection to set the values of fields in that class. This is typically done only in special circumstances when setting the values in the usual way is not possible. Because such access usually violates the design intentions of the class, it should be used with the utmost discretion.

The Book class illustrates how to set the values for long, array, and enum field types. Methods for getting and setting other primitive types are described in Field.

import java.lang.reflect.Field;import java.util.Arrays;import static java.lang.System.out;

enum Tweedle { DEE, DUM }

public class Book { public long chapters = 0; public String[] characters = { "Alice", "White Rabbit" }; public Tweedle twin = Tweedle.DEE;

public static void main(String... args) {Book book = new Book();String fmt = "%6S: %-12s = %s%n";

23

Page 24: Java Ref Election

try { Class<?> c = book.getClass();

Field chap = c.getDeclaredField("chapters");

out.format(fmt, "before", "chapters", book.chapters); chap.setLong(book, 12);

out.format(fmt, "after", "chapters", chap.getLong(book));

Field chars = c.getDeclaredField("characters");

out.format(fmt, "before", "characters",

Arrays.asList(book.characters)); String[] newChars = { "Queen", "King" }; chars.set(book, newChars); out.format(fmt, "after", "characters",

Arrays.asList(book.characters));

Field t = c.getDeclaredField("twin"); out.format(fmt, "before", "twin",

book.twin); t.set(book, Tweedle.DUM); out.format(fmt, "after", "twin",

t.get(book));

// production code should handle these exceptions more gracefully

} catch (NoSuchFieldException x) { x.printStackTrace();} catch (IllegalAccessException x) { x.printStackTrace();}

}}

This is the corresponding output:

$ java BookBEFORE: chapters = 0 AFTER: chapters = 12BEFORE: characters = [Alice, White Rabbit] AFTER: characters = [Queen, King]BEFORE: twin = DEE AFTER: twin = DUM

Note: Setting a field's value via reflection has a certain amount of performance overhead because various operations must occur such as validating access

24

Page 25: Java Ref Election

permissions. From the runtime's point of view, the effects are the same, and the operation is as atomic as if the value was changed in the class code directly.

Use of reflection can cause some runtime optimizations to be lost. For example, the following code is highly likely be optimized by a Java virtual machine:

int x = 1;x = 2;x = 3;

Equivalent code using Field.set*() may not.

Troubleshooting

Here are a few common problems encountered by developers with explanations for why the occur and how to resolve them.

IllegalArgumentException due to Inconvertible Types

The FieldTrouble example will generate an IllegalArgumentException. Field.setInt() is invoked to set a field that is of the reference type Integer with a value of primitive type. In the non-reflection equivalent Integer val = 42, the compiler would convert (or box) the primitive type 42 to a reference type as new Integer(42) so that its type checking will accept the statement. When using reflection, type checking only occurs at runtime so there is no opportunity to box the value.

import java.lang.reflect.Field;

public class FieldTrouble { public Integer val;

public static void main(String... args) {FieldTrouble ft = new FieldTrouble();try { Class<?> c = ft.getClass(); Field f = c.getDeclaredField("val");

f.setInt(ft, 42); // IllegalArgumentException

// production code should handle these exceptions more gracefully

} catch (NoSuchFieldException x) { x.printStackTrace();

25

Page 26: Java Ref Election

} catch (IllegalAccessException x) { x.printStackTrace();

} }}$ java FieldTroubleException in thread "main" java.lang.IllegalArgumentException: Can not setjava.lang.Object field FieldTrouble.val to (long)42 at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:146) at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:174) at sun.reflect.UnsafeObjectFieldAccessorImpl.setLong(UnsafeObjectFieldAccessorImpl.java:102) at java.lang.reflect.Field.setLong(Field.java:831) at FieldTrouble.main(FieldTrouble.java:11)

To eliminate this exception, the problematic line should be replaced by the following invocation of Field.set(Object obj, Object value):

f.set(ft, new Integer(43));

Tip: When using reflection to set or get a field, the compiler does not have an opportunity to perform boxing. It can only convert types that are related as described by the specification for Class.isAssignableFrom(). The example is expected to fail because isAssignableFrom() will return false in this test which can be used programmatically to verify whether a particular conversion is possible:

Integer.class.isAssignableFrom(int.class) == false

Similarly, automatic conversion from primitive to reference type is also impossible in reflection.

int.class.isAssignableFrom(Integer.class) == false

26

Page 27: Java Ref Election

NoSuchFieldException for Non-Public Fields

The astute reader may notice that if the FieldSpy example shown earlier is used to get information on a non-public field, it will fail:

$ java FieldSpy java.lang.String countjava.lang.NoSuchFieldException: count at java.lang.Class.getField(Class.java:1519) at FieldSpy.main(FieldSpy.java:12)

Tip: The Class.getField() and Class.getFields() methods return the public member field(s) of the class, enum, or interface represented by the Class object. To retrieve all fields declared (but not inherited) in the Class, use the Class.getDeclaredFields() method.

IllegalAccessException when Modifying Final Fields

An IllegalAccessException may be thrown if an attempt is made to get or set the value of a private or otherwise inaccessible field or to set the value of a final field (regardless of its access modifiers).

The FieldTroubleToo example illustrates the type of stack trace which results from attempting to set a final field.

import java.lang.reflect.Field;

public class FieldTroubleToo { public final boolean b = true;

public static void main(String... args) {FieldTroubleToo ft = new FieldTroubleToo();try { Class<?> c = ft.getClass(); Field f = c.getDeclaredField("b");

// f.setAccessible(true); // solution f.setBoolean(ft, Boolean.FALSE); //

IllegalAccessException

// production code should handle these exceptions more gracefully

} catch (NoSuchFieldException x) { x.printStackTrace();} catch (IllegalArgumentException x) {

27

Page 28: Java Ref Election

x.printStackTrace();} catch (IllegalAccessException x) { x.printStackTrace();}

}}$ java FieldTroubleToojava.lang.IllegalAccessException: Can not set final boolean field FieldTroubleToo.b to (boolean)false at sun.reflect.UnsafeFieldAccessorImpl.throwFinalFieldIllegalAccessException(UnsafeFieldAccessorImpl.java:55) at sun.reflect.UnsafeFieldAccessorImpl.throwFinalFieldIllegalAccessException(UnsafeFieldAccessorImpl.java:63) at sun.reflect.UnsafeQualifiedBooleanFieldAccessorImpl.setBoolean(UnsafeQualifiedBooleanFieldAccessorImpl.java:78) at java.lang.reflect.Field.setBoolean(Field.java:686) at FieldTroubleToo.main(FieldTroubleToo.java:12)

Tip: An access restriction exists which prevents final fields from being set after initialization of the class. However, Field is declared to extend AccessibleObject which provides the ability to suppress this check.

If AccessibleObject.setAccessible() succeeds, then subsequent operations on this field value will not fail do to this problem. This may have unexpected side-effects; for example, sometimes the original value will continue to be used by some sections of the application even though the value has been modified. AccessibleObject.setAccessible() will only succeed if the operation is allowed by the security context.

Methods

A method contains executable code which may be invoked. Methods are inherited and in non-reflective code behaviors such as overloading, overriding, and hiding are enforced by the compiler. In contrast, reflective code makes it possible for method selection to be restricted to a specific

28

Page 29: Java Ref Election

class without considering its superclasses. Superclass methods may be accessed but it is possible to determine their declaring class; this is impossible to discover programmatically without reflection and is the source of many subtle bugs.

The java.lang.reflect.Method class provides APIs to access information about a method's modifiers, return type, parameters, annotations, and thrown exceptions. It also be used to invoke methods. These topics are covered by the following sections:

Obtaining Method Type Information shows how to enumerate methods declared in a class and obtains type information

Retrieving and Parsing Method Modifiers describes how to access and decode modifiers and other information associated with the method

Invoking Methods illustrates how to execute a method and obtain its return value

Troubleshooting covers common errors encountered when finding or invoking methods

Obtaining Method Type Information

A method declaration includes the name, modifiers, parameters, return type, and list of throwable exceptions. The java.lang.reflect.Method class provides a way to obtain this information.

The MethodSpy example illustrates how to enumerate all of the declared methods in a given class and retrieve the return, parameter, and exception types for all the methods of the given name.

import java.lang.reflect.Method;import java.lang.reflect.Type;import static java.lang.System.out;

public class MethodSpy { private static final String fmt = "%24s: %s%n";

// for the morbidly curious <E extends RuntimeException> void genericThrow() throws E {}

public static void main(String... args) {try { Class<?> c = Class.forName(args[0]); Method[] allMethods =

c.getDeclaredMethods();

29

Page 30: Java Ref Election

for (Method m : allMethods) {if (!m.getName().equals(args[1])) { continue;}out.format("%s%n",

m.toGenericString());

out.format(fmt, "ReturnType", m.getReturnType());

out.format(fmt, "GenericReturnType", m.getGenericReturnType());

Class<?>[] pType = m.getParameterTypes();

Type[] gpType = m.getGenericParameterTypes();

for (int i = 0; i < pType.length; i++) {

out.format(fmt,"ParameterType", pType[i]);

out.format(fmt,"GenericParameterType", gpType[i]);

}

Class<?>[] xType = m.getExceptionTypes();

Type[] gxType = m.getGenericExceptionTypes();

for (int i = 0; i < xType.length; i++) {

out.format(fmt,"ExceptionType", xType[i]);

out.format(fmt,"GenericExceptionType", gxType[i]);

} }

// production code should handle these exceptions more gracefully

} catch (ClassNotFoundException x) { x.printStackTrace();}

}}

Here is the output for Class.getConstructor() which is an example of a method with parameterized types and a variable number of parameters.

$ java MethodSpy java.lang.Class getConstructorpublic java.lang.reflect.Constructor<T> java.lang.Class.getConstructor(java.lang.Class<?>[]) throws java.lang.NoSuchMethodException,java.lang.SecurityException

30

Page 31: Java Ref Election

ReturnType: class java.lang.reflect.Constructor GenericReturnType: java.lang.reflect.Constructor<T> ParameterType: class [Ljava.lang.Class; GenericParameterType: java.lang.Class<?>[] ExceptionType: class java.lang.NoSuchMethodException GenericExceptionType: class java.lang.NoSuchMethodException ExceptionType: class java.lang.SecurityException GenericExceptionType: class java.lang.SecurityException

This is the actual declaration of the method in source code:

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

First note that the return and parameter types are generic. Method.getGenericReturnType() will consult the Signature Attribute in the class file if it's present. If the attribute isn't available, it falls back on Method.getReturnType() which was not changed by the introduction of generics. The other methods with name getGenericFoo() for some value of Foo in reflection are implemented similarly.

Next, notice that the last (and only) parameter, parameterType, is of variable arity (has a variable number of parameters) of type java.lang.Class. It is represented as a single-dimension array of type java.lang.Class. This can be distinguished from a parameter that is explicitly an array of java.lang.Class by invoking Method.isVarArgs(). The syntax for the returned values of Method.get*Types() is described in Class.getName().

The following example illustrates a method with a generic return type.

$ java MethodSpy java.lang.Class castpublic T java.lang.Class.cast(java.lang.Object) ReturnType: class java.lang.Object GenericReturnType: T ParameterType: class java.lang.Object GenericParameterType: class java.lang.Object

The generic return type for the method Class.cast() is reported as java.lang.Object because generics are implemented via type erasure which removes all information regarding generic types during compilation. The erasure of T is defined by the declaration of Class:

31

Page 32: Java Ref Election

public final class Class<T> implements ...

Thus T is replaced by the upper bound of the type variable, in this case, java.lang.Object.

The last example illustrates the output for a method with multiple overloads.

$ java MethodSpy java.io.PrintStream formatpublic java.io.PrintStream java.io.PrintStream.format(java.util.Locale,java.lang.String,java.lang.Object[]) ReturnType: class java.io.PrintStream GenericReturnType: class java.io.PrintStream ParameterType: class java.util.Locale GenericParameterType: class java.util.Locale ParameterType: class java.lang.String GenericParameterType: class java.lang.String ParameterType: class [Ljava.lang.Object; GenericParameterType: class [Ljava.lang.Object;public java.io.PrintStream java.io.PrintStream.format(java.lang.String,java.lang.Object[]) ReturnType: class java.io.PrintStream GenericReturnType: class java.io.PrintStream ParameterType: class java.lang.String GenericParameterType: class java.lang.String ParameterType: class [Ljava.lang.Object; GenericParameterType: class [Ljava.lang.Object;

If multiple overloads of the same method name are discovered, they are all returned by Class.getDeclaredMethods(). Since format() has two overloads (with with a Locale and one without), both are shown by MethodSpy.

Note: Method.getGenericExceptionTypes() exists because it is actually possible to declare a method with a generic exception type. However this is rarely used since it is not possible to catch a generic exception type.

32

Page 33: Java Ref Election

Retrieving and Parsing Method Modifiers

There a several modifiers that may be part of a method declaration:

Access modifiers: public, protected, and private Modifier restricting to one instance: static Modifier prohibiting value modification: final Modifier requiring override: abstract Modifier preventing reentrancy: synchronized Modifier indicating implementation in another programming

language: native Modifier forcing strict floating point behavior: strictfp Annotations

The MethodModifierSpy example lists the modifiers of a method with a given name. It also displays whether the method is synthetic (compiler-generated), of variable arity, or a bridge method (compiler-generated to support generic interfaces).

import java.lang.reflect.Method;import java.lang.reflect.Modifier;import static java.lang.System.out;

public class MethodModifierSpy {

private static int count; private static synchronized void inc() { count++; } private static synchronized int cnt() { return count; }

public static void main(String... args) {try { Class<?> c = Class.forName(args[0]); Method[] allMethods =

c.getDeclaredMethods(); for (Method m : allMethods) {

if (!m.getName().equals(args[1])) { continue;}out.format("%s%n",

m.toGenericString());out.format(" Modifiers: %s%n",

Modifier.toString(m.getModifiers()));

out.format(" [ synthetic=%-5b var_args=%-5b bridge=%-5b ]%n",

m.isSynthetic(), m.isVarArgs(), m.isBridge());

inc(); }

33

Page 34: Java Ref Election

out.format("%d matching overload%s found%n", cnt(),

(cnt() == 1 ? "" : "s"));

// production code should handle this exception more gracefully

} catch (ClassNotFoundException x) { x.printStackTrace();}

}}

A few examples of the output MethodMdifierSpy produces follow.

$ java MethodModifierSpy java.lang.Object waitpublic final native void java.lang.Object.wait(long) throws java.lang.InterruptedException Modifiers: public final native [ synthetic=false var_args=false bridge=false ]public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException Modifiers: public final [ synthetic=false var_args=false bridge=false ]public final void java.lang.Object.wait() throws java.lang.InterruptedException Modifiers: public final [ synthetic=false var_args=false bridge=false ]3 matching overloads found$ java MethodModifierSpy java.lang.StrictMath toRadianspublic static double java.lang.StrictMath.toRadians(double) Modifiers: public static strictfp [ synthetic=false var_args=false bridge=false ]1 matching overload found$ java MethodModifierSpy MethodModifierSpy incprivate synchronized void MethodModifierSpy.inc() Modifiers: private synchronized [ synthetic=false var_args=false bridge=false ]1 matching overload found$ java MethodModifierSpy java.lang.Class getConstructorpublic java.lang.reflect.Constructor<T> java.lang.Class.getConstructor(java.lang.Class<T>[]) throws java.lang.NoSuchMethodException,java.lang.SecurityException Modifiers: public transient

34

Page 35: Java Ref Election

[ synthetic=false var_args=true bridge=false ]1 matching overload found$ java MethodModifierSpy java.lang.String compareTopublic int java.lang.String.compareTo(java.lang.String) Modifiers: public [ synthetic=false var_args=false bridge=false ]public int java.lang.String.compareTo(java.lang.Object) Modifiers: public volatile [ synthetic=true var_args=false bridge=true ]2 matching overloads found

Note that Method.isVarArgs() returns true for Class.getConstructor(). This indicates that the method declaration looks like this:

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

not like this:

public Constructor<T> getConstructor(Class<?> [] parameterTypes)

Notice that the output for String.compareTo() contains two methods. The method declared in String.java:

public int compareTo(String anotherString);

and a second synthetic or compiler-generated bridge method. This occurs because String implements the parameterized interface Comparable. During type erasure, the argument type of the inherited method Comparable.compareTo() is changed from java.lang.Object to java.lang.String. Since the parameter types for the compareTo methods in Comparable and String no longer match after erasure, overriding can not occur. In all other circumstances, this would produce a compile-time error because the interface is not implemented. The addition of the bridge method avoids this problem.

Method implements java.lang.reflect.AnnotatedElement. Thus any runtime annotations with java.lang.annotation.RetentionPolicy.RUNTIME may be retrieved. For an example of obtaining annotations see the section Examining Class Modifiers and Types.

35

Page 36: Java Ref Election

Getting and Setting Field Values

Given an instance of a class, it is possible to use reflection to set the values of fields in that class. This is typically done only in special circumstances when setting the values in the usual way is not possible. Because such access usually violates the design intentions of the class, it should be used with the utmost discretion.

The Book class illustrates how to set the values for long, array, and enum field types. Methods for getting and setting other primitive types are described in Field.

import java.lang.reflect.Field;import java.util.Arrays;import static java.lang.System.out;

enum Tweedle { DEE, DUM }

public class Book { public long chapters = 0; public String[] characters = { "Alice", "White Rabbit" }; public Tweedle twin = Tweedle.DEE;

public static void main(String... args) {Book book = new Book();String fmt = "%6S: %-12s = %s%n";

try { Class<?> c = book.getClass();

Field chap = c.getDeclaredField("chapters");

out.format(fmt, "before", "chapters", book.chapters); chap.setLong(book, 12);

out.format(fmt, "after", "chapters", chap.getLong(book));

Field chars = c.getDeclaredField("characters");

out.format(fmt, "before", "characters",

Arrays.asList(book.characters)); String[] newChars = { "Queen", "King" }; chars.set(book, newChars); out.format(fmt, "after", "characters",

Arrays.asList(book.characters));

36

Page 37: Java Ref Election

Field t = c.getDeclaredField("twin"); out.format(fmt, "before", "twin",

book.twin); t.set(book, Tweedle.DUM); out.format(fmt, "after", "twin",

t.get(book));

// production code should handle these exceptions more gracefully

} catch (NoSuchFieldException x) { x.printStackTrace();} catch (IllegalAccessException x) { x.printStackTrace();}

}}

This is the corresponding output:

$ java BookBEFORE: chapters = 0 AFTER: chapters = 12BEFORE: characters = [Alice, White Rabbit] AFTER: characters = [Queen, King]BEFORE: twin = DEE AFTER: twin = DUM

Note: Setting a field's value via reflection has a certain amount of performance overhead because various operations must occur such as validating access permissions. From the runtime's point of view, the effects are the same, and the operation is as atomic as if the value was changed in the class code directly.

Use of reflection can cause some runtime optimizations to be lost. For example, the following code is highly likely be optimized by a Java virtual machine:

int x = 1;x = 2;x = 3;

Equivalent code using Field.set*() may not.

Troubleshooting

37

Page 38: Java Ref Election

Here are a few common problems encountered by developers with explanations for why the occur and how to resolve them.

IllegalArgumentException due to Inconvertible Types

The FieldTrouble example will generate an IllegalArgumentException. Field.setInt() is invoked to set a field that is of the reference type Integer with a value of primitive type. In the non-reflection equivalent Integer val = 42, the compiler would convert (or box) the primitive type 42 to a reference type as new Integer(42) so that its type checking will accept the statement. When using reflection, type checking only occurs at runtime so there is no opportunity to box the value.

import java.lang.reflect.Field;

public class FieldTrouble { public Integer val;

public static void main(String... args) {FieldTrouble ft = new FieldTrouble();try { Class<?> c = ft.getClass(); Field f = c.getDeclaredField("val");

f.setInt(ft, 42); // IllegalArgumentException

// production code should handle these exceptions more gracefully

} catch (NoSuchFieldException x) { x.printStackTrace();

} catch (IllegalAccessException x) { x.printStackTrace();

} }}$ java FieldTroubleException in thread "main" java.lang.IllegalArgumentException: Can not setjava.lang.Object field FieldTrouble.val to (long)42 at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:146) at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:174) at sun.reflect.UnsafeObjectFieldAccessorImpl.setLong(UnsafeObjectFieldAccessorImpl.java:102)

38

Page 39: Java Ref Election

at java.lang.reflect.Field.setLong(Field.java:831) at FieldTrouble.main(FieldTrouble.java:11)

To eliminate this exception, the problematic line should be replaced by the following invocation of Field.set(Object obj, Object value):

f.set(ft, new Integer(43));

Tip: When using reflection to set or get a field, the compiler does not have an opportunity to perform boxing. It can only convert types that are related as described by the specification for Class.isAssignableFrom(). The example is expected to fail because isAssignableFrom() will return false in this test which can be used programmatically to verify whether a particular conversion is possible:

Integer.class.isAssignableFrom(int.class) == false

Similarly, automatic conversion from primitive to reference type is also impossible in reflection.

int.class.isAssignableFrom(Integer.class) == false

NoSuchFieldException for Non-Public Fields

The astute reader may notice that if the FieldSpy example shown earlier is used to get information on a non-public field, it will fail:

$ java FieldSpy java.lang.String countjava.lang.NoSuchFieldException: count at java.lang.Class.getField(Class.java:1519) at FieldSpy.main(FieldSpy.java:12)

Tip: The Class.getField() and Class.getFields() methods return the public member field(s) of the class, enum, or interface represented by the Class object. To

39

Page 40: Java Ref Election

retrieve all fields declared (but not inherited) in the Class, use the Class.getDeclaredFields() method.

IllegalAccessException when Modifying Final Fields

An IllegalAccessException may be thrown if an attempt is made to get or set the value of a private or otherwise inaccessible field or to set the value of a final field (regardless of its access modifiers).

The FieldTroubleToo example illustrates the type of stack trace which results from attempting to set a final field.

import java.lang.reflect.Field;

public class FieldTroubleToo { public final boolean b = true;

public static void main(String... args) {FieldTroubleToo ft = new FieldTroubleToo();try { Class<?> c = ft.getClass(); Field f = c.getDeclaredField("b");

// f.setAccessible(true); // solution f.setBoolean(ft, Boolean.FALSE); //

IllegalAccessException

// production code should handle these exceptions more gracefully

} catch (NoSuchFieldException x) { x.printStackTrace();} catch (IllegalArgumentException x) { x.printStackTrace();} catch (IllegalAccessException x) { x.printStackTrace();}

}}$ java FieldTroubleToojava.lang.IllegalAccessException: Can not set final boolean field FieldTroubleToo.b to (boolean)false at sun.reflect.UnsafeFieldAccessorImpl.throwFinalFieldIllegalAccessException(UnsafeFieldAccessorImpl.java:55) at sun.reflect.UnsafeFieldAccessorImpl.throwFinalFieldIllegalAccessException(UnsafeFieldAccessorImpl.java:63)

40

Page 41: Java Ref Election

at sun.reflect.UnsafeQualifiedBooleanFieldAccessorImpl.setBoolean(UnsafeQualifiedBooleanFieldAccessorImpl.java:78) at java.lang.reflect.Field.setBoolean(Field.java:686) at FieldTroubleToo.main(FieldTroubleToo.java:12)

Tip: An access restriction exists which prevents final fields from being set after initialization of the class. However, Field is declared to extend AccessibleObject which provides the ability to suppress this check.

If AccessibleObject.setAccessible() succeeds, then subsequent operations on this field value will not fail do to this problem. This may have unexpected side-effects; for example, sometimes the original value will continue to be used by some sections of the application even though the value has been modified. AccessibleObject.setAccessible() will only succeed if the operation is allowed by the security context.

Methods

A method contains executable code which may be invoked. Methods are inherited and in non-reflective code behaviors such as overloading, overriding, and hiding are enforced by the compiler. In contrast, reflective code makes it possible for method selection to be restricted to a specific class without considering its superclasses. Superclass methods may be accessed but it is possible to determine their declaring class; this is impossible to discover programmatically without reflection and is the source of many subtle bugs.

The java.lang.reflect.Method class provides APIs to access information about a method's modifiers, return type, parameters, annotations, and thrown exceptions. It also be used to invoke methods. These topics are covered by the following sections:

Obtaining Method Type Information shows how to enumerate methods declared in a class and obtains type information

41

Page 42: Java Ref Election

Retrieving and Parsing Method Modifiers describes how to access and decode modifiers and other information associated with the method

Invoking Methods illustrates how to execute a method and obtain its return value

Troubleshooting covers common errors encountered when finding or invoking methods

Obtaining Method Type Information

A method declaration includes the name, modifiers, parameters, return type, and list of throwable exceptions. The java.lang.reflect.Method class provides a way to obtain this information.

The MethodSpy example illustrates how to enumerate all of the declared methods in a given class and retrieve the return, parameter, and exception types for all the methods of the given name.

import java.lang.reflect.Method;import java.lang.reflect.Type;import static java.lang.System.out;

public class MethodSpy { private static final String fmt = "%24s: %s%n";

// for the morbidly curious <E extends RuntimeException> void genericThrow() throws E {}

public static void main(String... args) {try { Class<?> c = Class.forName(args[0]); Method[] allMethods =

c.getDeclaredMethods(); for (Method m : allMethods) {

if (!m.getName().equals(args[1])) { continue;}out.format("%s%n",

m.toGenericString());

out.format(fmt, "ReturnType", m.getReturnType());

out.format(fmt, "GenericReturnType", m.getGenericReturnType());

Class<?>[] pType = m.getParameterTypes();

42

Page 43: Java Ref Election

Type[] gpType = m.getGenericParameterTypes();

for (int i = 0; i < pType.length; i++) {

out.format(fmt,"ParameterType", pType[i]);

out.format(fmt,"GenericParameterType", gpType[i]);

}

Class<?>[] xType = m.getExceptionTypes();

Type[] gxType = m.getGenericExceptionTypes();

for (int i = 0; i < xType.length; i++) {

out.format(fmt,"ExceptionType", xType[i]);

out.format(fmt,"GenericExceptionType", gxType[i]);

} }

// production code should handle these exceptions more gracefully

} catch (ClassNotFoundException x) { x.printStackTrace();}

}}

Here is the output for Class.getConstructor() which is an example of a method with parameterized types and a variable number of parameters.

$ java MethodSpy java.lang.Class getConstructorpublic java.lang.reflect.Constructor<T> java.lang.Class.getConstructor(java.lang.Class<?>[]) throws java.lang.NoSuchMethodException,java.lang.SecurityException ReturnType: class java.lang.reflect.Constructor GenericReturnType: java.lang.reflect.Constructor<T> ParameterType: class [Ljava.lang.Class; GenericParameterType: java.lang.Class<?>[] ExceptionType: class java.lang.NoSuchMethodException GenericExceptionType: class java.lang.NoSuchMethodException ExceptionType: class java.lang.SecurityException

43

Page 44: Java Ref Election

GenericExceptionType: class java.lang.SecurityException

This is the actual declaration of the method in source code:

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

First note that the return and parameter types are generic. Method.getGenericReturnType() will consult the Signature Attribute in the class file if it's present. If the attribute isn't available, it falls back on Method.getReturnType() which was not changed by the introduction of generics. The other methods with name getGenericFoo() for some value of Foo in reflection are implemented similarly.

Next, notice that the last (and only) parameter, parameterType, is of variable arity (has a variable number of parameters) of type java.lang.Class. It is represented as a single-dimension array of type java.lang.Class. This can be distinguished from a parameter that is explicitly an array of java.lang.Class by invoking Method.isVarArgs(). The syntax for the returned values of Method.get*Types() is described in Class.getName().

The following example illustrates a method with a generic return type.

$ java MethodSpy java.lang.Class castpublic T java.lang.Class.cast(java.lang.Object) ReturnType: class java.lang.Object GenericReturnType: T ParameterType: class java.lang.Object GenericParameterType: class java.lang.Object

The generic return type for the method Class.cast() is reported as java.lang.Object because generics are implemented via type erasure which removes all information regarding generic types during compilation. The erasure of T is defined by the declaration of Class:

public final class Class<T> implements ...

Thus T is replaced by the upper bound of the type variable, in this case, java.lang.Object.

The last example illustrates the output for a method with multiple overloads.

$ java MethodSpy java.io.PrintStream formatpublic java.io.PrintStream java.io.PrintStream.format(java.util.Locale,java.lang.String,java.lang.Object[])

44

Page 45: Java Ref Election

ReturnType: class java.io.PrintStream GenericReturnType: class java.io.PrintStream ParameterType: class java.util.Locale GenericParameterType: class java.util.Locale ParameterType: class java.lang.String GenericParameterType: class java.lang.String ParameterType: class [Ljava.lang.Object; GenericParameterType: class [Ljava.lang.Object;public java.io.PrintStream java.io.PrintStream.format(java.lang.String,java.lang.Object[]) ReturnType: class java.io.PrintStream GenericReturnType: class java.io.PrintStream ParameterType: class java.lang.String GenericParameterType: class java.lang.String ParameterType: class [Ljava.lang.Object; GenericParameterType: class [Ljava.lang.Object;

If multiple overloads of the same method name are discovered, they are all returned by Class.getDeclaredMethods(). Since format() has two overloads (with with a Locale and one without), both are shown by MethodSpy.

Note: Method.getGenericExceptionTypes() exists because it is actually possible to declare a method with a generic exception type. However this is rarely used since it is not possible to catch a generic exception type.

Retrieving and Parsing Method Modifiers

There a several modifiers that may be part of a method declaration:

Access modifiers: public, protected, and private Modifier restricting to one instance: static Modifier prohibiting value modification: final Modifier requiring override: abstract Modifier preventing reentrancy: synchronized Modifier indicating implementation in another programming

language: native

45

Page 46: Java Ref Election

Modifier forcing strict floating point behavior: strictfp Annotations

The MethodModifierSpy example lists the modifiers of a method with a given name. It also displays whether the method is synthetic (compiler-generated), of variable arity, or a bridge method (compiler-generated to support generic interfaces).

import java.lang.reflect.Method;import java.lang.reflect.Modifier;import static java.lang.System.out;

public class MethodModifierSpy {

private static int count; private static synchronized void inc() { count++; } private static synchronized int cnt() { return count; }

public static void main(String... args) {try { Class<?> c = Class.forName(args[0]); Method[] allMethods =

c.getDeclaredMethods(); for (Method m : allMethods) {

if (!m.getName().equals(args[1])) { continue;}out.format("%s%n",

m.toGenericString());out.format(" Modifiers: %s%n",

Modifier.toString(m.getModifiers()));

out.format(" [ synthetic=%-5b var_args=%-5b bridge=%-5b ]%n",

m.isSynthetic(), m.isVarArgs(), m.isBridge());

inc(); } out.format("%d matching overload%s found

%n", cnt(), (cnt() == 1 ? "" : "s"));

// production code should handle this exception more gracefully

} catch (ClassNotFoundException x) { x.printStackTrace();}

}}

A few examples of the output MethodMdifierSpy produces follow.

46

Page 47: Java Ref Election

$ java MethodModifierSpy java.lang.Object waitpublic final native void java.lang.Object.wait(long) throws java.lang.InterruptedException Modifiers: public final native [ synthetic=false var_args=false bridge=false ]public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException Modifiers: public final [ synthetic=false var_args=false bridge=false ]public final void java.lang.Object.wait() throws java.lang.InterruptedException Modifiers: public final [ synthetic=false var_args=false bridge=false ]3 matching overloads found$ java MethodModifierSpy java.lang.StrictMath toRadianspublic static double java.lang.StrictMath.toRadians(double) Modifiers: public static strictfp [ synthetic=false var_args=false bridge=false ]1 matching overload found$ java MethodModifierSpy MethodModifierSpy incprivate synchronized void MethodModifierSpy.inc() Modifiers: private synchronized [ synthetic=false var_args=false bridge=false ]1 matching overload found$ java MethodModifierSpy java.lang.Class getConstructorpublic java.lang.reflect.Constructor<T> java.lang.Class.getConstructor(java.lang.Class<T>[]) throws java.lang.NoSuchMethodException,java.lang.SecurityException Modifiers: public transient [ synthetic=false var_args=true bridge=false ]1 matching overload found$ java MethodModifierSpy java.lang.String compareTopublic int java.lang.String.compareTo(java.lang.String) Modifiers: public [ synthetic=false var_args=false bridge=false ]public int java.lang.String.compareTo(java.lang.Object) Modifiers: public volatile [ synthetic=true var_args=false bridge=true ]

47

Page 48: Java Ref Election

2 matching overloads found

Note that Method.isVarArgs() returns true for Class.getConstructor(). This indicates that the method declaration looks like this:

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

not like this:

public Constructor<T> getConstructor(Class<?> [] parameterTypes)

Notice that the output for String.compareTo() contains two methods. The method declared in String.java:

public int compareTo(String anotherString);

and a second synthetic or compiler-generated bridge method. This occurs because String implements the parameterized interface Comparable. During type erasure, the argument type of the inherited method Comparable.compareTo() is changed from java.lang.Object to java.lang.String. Since the parameter types for the compareTo methods in Comparable and String no longer match after erasure, overriding can not occur. In all other circumstances, this would produce a compile-time error because the interface is not implemented. The addition of the bridge method avoids this problem.

Method implements java.lang.reflect.AnnotatedElement. Thus any runtime annotations with java.lang.annotation.RetentionPolicy.RUNTIME may be retrieved. For an example of obtaining annotations see the section Examining Class Modifiers and Types.

Invoking Methods

Reflection provides a means for invoking methods on a class. Typically, this would only be necessary if it is not possible to cast an instance of the class to the desired type in non-reflective code. Methods are invoked with java.lang.reflect.Method.invoke(). The first argument is the object instance on which this particular method is to be invoked. (If the method is static, the first argument should be null.) Subsequent arguments are the method's parameters. If the underlying method throws an exception, it will be wrapped by an java.lang.reflect.InvocationTargetException.

48

Page 49: Java Ref Election

The method's original exception may be retrieved using the exception chaining mechanism's InvocationTargetException.getCause() method.

Finding and Invoking a Method with a Specific Declaration

Consider a test suite which uses reflection to invoke private test methods in a given class. The Deet example searches for public methods in a class which begin with the string "test", have a boolean return type, and a single Locale parameter. It then invokes each matching method.

import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;import java.lang.reflect.Type;import java.util.Locale;import static java.lang.System.out;import static java.lang.System.err;

public class Deet<T> { private boolean testDeet(Locale l) {

// getISO3Language() may throw a MissingResourceException

out.format("Locale = %s, ISO Language Code = %s%n", l.getDisplayName(), l.getISO3Language());

return true; }

private int testFoo(Locale l) { return 0; } private boolean testBar() { return true; }

public static void main(String... args) {if (args.length != 4) { err.format("Usage: java Deet <classname>

<langauge> <country> <variant>%n"); return;}

try { Class<?> c = Class.forName(args[0]); Object t = c.newInstance();

Method[] allMethods = c.getDeclaredMethods();

for (Method m : allMethods) {String mname = m.getName();if (!mname.startsWith("test") || (m.getGenericReturnType() !=

boolean.class)) { continue;}

Type[] pType = m.getGenericParameterTypes();

49

Page 50: Java Ref Election

if ((pType.length != 1) ||

Locale.class.isAssignableFrom(pType[0].getClass())) { continue; }

out.format("invoking %s()%n", mname);try { m.setAccessible(true); Object o = m.invoke(t, new

Locale(args[1], args[2], args[3])); out.format("%s() returned %b%n",

mname, (Boolean) o);

// Handle any exceptions thrown by method to be invoked.

} catch (InvocationTargetException x) {

Throwable cause = x.getCause(); err.format("invocation of %s

failed: %s%n", mname,

cause.getMessage());}

}

// production code should handle these exceptions more gracefully

} catch (ClassNotFoundException x) { x.printStackTrace();} catch (InstantiationException x) { x.printStackTrace();} catch (IllegalAccessException x) { x.printStackTrace();}

}}

Deet invokes getDeclaredMethods() which will return all methods explicitly declared in the class. Also, Class.isAssignableFrom() is used to determine whether the parameters of the located method are compatible with the desired invocation. Technically the code could have tested whether the following statement is true since Locale is final:

Locale.class == pType[0].getClass()

However, Class.isAssignableFrom() is more general.

$ java Deet Deet ja JP JPinvoking testDeet()Locale = Japanese (Japan,JP), ISO Language Code = jpntestDeet() returned true

50

Page 51: Java Ref Election

$ java Deet Deet xx XX XXinvoking testDeet()invocation of testDeet failed: Couldn't find 3-letter language code for xx

First, note that only testDeet() meets the declaration restrictions enforced by the code. Next, when testDeet() is passed an invalid argument it throws an unchecked java.util.MissingResourceException. In reflection, there is no distinction in the handling of checked versus unchecked exceptions. They are all wrapped in an InvocationTargetException

Invoking Methods with a Variable Number of Arguments

Method.invoke() may be used to pass a variable number of arguments to a method. The key concept to understand is that methods of variable arity are implemented as if the variable arguments are packed in an array.

The InvokeMain example illustrates how to invoke the main() entry point in any class and pass a set of arguments determined at runtime.

import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;import java.util.Arrays;

public class InvokeMain { public static void main(String... args) {

try { Class<?> c = Class.forName(args[0]); Class[] argTypes = new Class[]

{ String[].class }; Method main =

c.getDeclaredMethod("main", argTypes); String[] mainArgs = Arrays.copyOfRange(args, 1, args.length);

System.out.format("invoking %s.main()%n", c.getName());

main.invoke(null, (Object)mainArgs);

// production code should handle these exceptions more gracefully

} catch (ClassNotFoundException x) { x.printStackTrace();} catch (NoSuchMethodException x) { x.printStackTrace();} catch (IllegalAccessException x) { x.printStackTrace();} catch (InvocationTargetException x) { x.printStackTrace();}

51

Page 52: Java Ref Election

}}

First, to find the main() method the code searches for a class with the name "main" with a single parameter that is an array of String Since main() is static, null is the first argument to Method.invoke(). The second argument is the array of arguments to be passed.

$ java InvokeMain Deet Deet ja JP JPinvoking Deet.main()invoking testDeet()Locale = Japanese (Japan,JP), ISO Language Code = jpntestDeet() returned true

This section contains examples of problems developers might encounter when using reflection to locate, invoke, or get information about methods.

NoSuchMethodException Due to Type Erasure

The MethodTrouble example illustrates what happens when type erasure is not taken into consideration by code which searches for a particular method in a class.

import java.lang.reflect.Method;

public class MethodTrouble<T> { public void lookup(T t) {} public void find(Integer i) {}

public static void main(String... args) {try { String mName = args[0]; Class cArg = Class.forName(args[1]); Class<?> c = (new

MethodTrouble<Integer>()).getClass(); Method m = c.getMethod(mName, cArg); System.out.format("Found:%n %s%n",

m.toGenericString());

// production code should handle these exceptions more gracefully} catch (NoSuchMethodException x) { x.printStackTrace();} catch (ClassNotFoundException x) { x.printStackTrace();}

}}$ java MethodTrouble lookup java.lang.Integer

52

Page 53: Java Ref Election

java.lang.NoSuchMethodException: MethodTrouble.lookup(java.lang.Integer) at java.lang.Class.getMethod(Class.java:1605) at MethodTrouble.main(MethodTrouble.java:12)$ java MethodTrouble lookup java.lang.ObjectFound: public void MethodTrouble.lookup(T)

When a method is declared with a generic parameter type, the compiler will replace the generic type with its upper bound, in this case, the upper bound of T is Object. Thus, when the code searches for lookup(Integer), no method is found, despite the fact that the instance of MethodTrouble was created as follows:

Class c = (new MethodTrouble()).getClass();

Searching for lookup(Object) succeeds as expected.

$ java MethodTrouble find java.lang.IntegerFound: public void MethodTrouble.find(java.lang.Integer)$ java MethodTrouble find java.lang.Objectjava.lang.NoSuchMethodException: MethodTrouble.find(java.lang.Object) at java.lang.Class.getMethod(Class.java:1605) at MethodTrouble.main(MethodTrouble.java:12)

In this case, find() has no generic parameters, so the parameter types searched for by getMethod() must match exactly.

Tip: Always pass the upper bound of the parameterized type when searching for a method.

IllegalAccessException when Invoking a Method

An IllegalAccessException is thrown if an attempt is made to invoke a private or otherwise inaccessible method.

The MethodTroubleAgain example shows a typical stack trace which results from trying to invoke a private method in an another class.

import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;

class AnotherClass { private void m() {}

53

Page 54: Java Ref Election

}

public class MethodTroubleAgain { public static void main(String... args) {AnotherClass ac = new AnotherClass();try { Class<?> c = ac.getClass();

Method m = c.getDeclaredMethod("m");// m.setAccessible(true); // solution Object o = m.invoke(ac); // IllegalAccessException

// production code should handle these exceptions more gracefully} catch (NoSuchMethodException x) { x.printStackTrace();} catch (InvocationTargetException x) { x.printStackTrace();} catch (IllegalAccessException x) { x.printStackTrace();}

}}

The stack trace for the exception thrown follows.

$ java MethodTroubleAgainjava.lang.IllegalAccessException: Class MethodTroubleAgain can not access a member of class AnotherClass with modifiers "private" at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:65) at java.lang.reflect.Method.invoke(Method.java:588) at MethodTroubleAgain.main(MethodTroubleAgain.java:15)

Tip: An access restriction exists which prevents reflective invocation of methods which normally would not be accessible via direct invocation. (This includes---but is not limited to---private methods in a separate class and public methods in a separate private class.) However, Method is declared to extend AccessibleObject which provides the ability to suppress this check via AccessibleObject.setAccessible(). If it succeeds, then subsequent invocations of this method object will not fail due to this problem.

IllegalArgumentException from Method.invoke()

54

Page 55: Java Ref Election

Method.invoke() has been retrofitted to be a variable-arity method. This is an enormous convenience, however it can lead to unexpected behavior. The MethodTroubleToo example shows various ways in which Method.invoke() can produce confusing results.

import java.lang.reflect.Method;

public class MethodTroubleToo { public void ping() { System.out.format("PONG!%n"); }

public static void main(String... args) {try { MethodTroubleToo mtt = new MethodTroubleToo(); Method m = MethodTroubleToo.class.getMethod("ping");

switch(Integer.parseInt(args[0])) { case 0:

m.invoke(mtt); // worksbreak;

case 1: m.invoke(mtt, null); // works (expect compiler warning)

break; case 2:

Object arg2 = null;m.invoke(mtt, arg2); //

IllegalArgumentExceptionbreak;

case 3:m.invoke(mtt, new Object[0]); // worksbreak;

case 4:Object arg4 = new Object[0];m.invoke(mtt, arg4); //

IllegalArgumentExceptionbreak;

default:System.out.format("Test not found%n");

}

// production code should handle these exceptions more gracefully} catch (Exception x) { x.printStackTrace();}

}}$ java MethodTroubleToo 0PONG!

Since all of the parameters of Method.invoke() are optional except for the first, they can be omitted when the method to be invoked has no parameters.

$ java MethodTroubleToo 1PONG!

55

Page 56: Java Ref Election

The code in this case generates this compiler warning because null is ambiguous.

$ javac MethodTroubleToo.javaMethodTroubleToo.java:16: warning: non-varargs call of varargs method with inexact argument type for last parameter;cast to java.lang.Object for a varargs callcast to java.lang.Object[] for a non-varargs call and to suppress this warning m.invoke(mtt, null); // works (expect compiler warning) ^1 warning

It is not possible to determine whether null represents an empty array of arguments or a first argument of null.

$ java MethodTroubleToo 2java.lang.IllegalArgumentException: wrong number of arguments at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:597) at MethodTroubleToo.main(MethodTroubleToo.java:21)

This fails despite the fact that the argument is null, because the type is a Object and ping() expects no arguments at all.

$ java MethodTroubleToo 3PONG!

This works because new Object[0] creates an empty array, and to a varargs method, this is equivalent to not passing any of the optional arguments.

$ java MethodTroubleToo 4java.lang.IllegalArgumentException: wrong number of arguments at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:597) at MethodTroubleToo.main(MethodTroubleToo.java:28)

56

Page 57: Java Ref Election

Unlike the previous example, if the empty array is stored in an Object, then it is treated as an Object. This fails for the same reason that case 2 fails, ping() does not expect an argument.

Tip: When a method foo(Object... o) is declared the compiler will put all of the arguments passed to foo() in an array of type Object. The implementation of foo() is the same as if it were declared foo(Object[] o). Understanding this may help avoid the types of problems illustrated above.

InvocationTargetException when Invoked Method Fails

An InvocationTargetException wraps all exceptions (checked and unchecked) produced when a method object is invoked. The MethodTroubleReturns example shows how to retrieve the original exception thrown by the invoked method.

import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;

public class MethodTroubleReturns { private void drinkMe(int liters) {if (liters < 0) throw new IllegalArgumentException("I can't drink a

negative amount of liquid"); }

public static void main(String... args) {try { MethodTroubleReturns mtr = new

MethodTroubleReturns(); Class<?> c = mtr.getClass(); Method m = c.getDeclaredMethod("drinkMe", int.class); m.invoke(mtr, -1);

// production code should handle these exceptions more gracefully} catch (InvocationTargetException x) { Throwable cause = x.getCause(); System.err.format("drinkMe() failed: %s%n",

cause.getMessage());} catch (Exception x) { x.printStackTrace();}

}}$ java MethodTroubleReturns

57

Page 58: Java Ref Election

drinkMe() failed: I can't drink a negative amount of liquid

Tip: If an InvocationTargetException is thrown, the method was invoked. Diagnosis of the problem would be the same as if the method was called directly and threw the exception that is retrieved by getCause(). This exception does not indicate a problem with the reflection package or its usage.

Constructors

A constructor is used in the creation of an object that is an instance of a class. Typically it performs operations required to initialize the class before methods are invoked or fields are accessed. Constructors are never inherited.

Similar to methods, reflection provides APIs to discover and retrieve the constructors of a class and obtain declaration information such as the modifiers, parameters, annotations, and thrown exceptions. New instances of classes may also be created using a specified constructor. The key classes used when working with constructors are Class and java.lang.reflect.Constructor. Common operations involving constructors are covered in the following sections:

Finding Constructors illustrates how to retrieve constructors with specific parameters

Retrieving and Parsing Constructor Modifiers shows how to obtain the modifiers of a constructor declaration and other information about the constructor

Creating New Class Instances shows how to instantiate an instance of an object by invoking its constructor

Troubleshooting describes common errors which may be encountered while finding or invoking constructors

Finding Constructors

A constructor declaration includes the name, modifiers, parameters, and list of throwable exceptions. The java.lang.reflect.Constructor class provides a way to obtain this information.

58

Page 59: Java Ref Election

The ConstructorSift example illustrates how to search a class's declared constructors for one which has a parameter of a given type.

import java.lang.reflect.Constructor;import java.lang.reflect.Type;import static java.lang.System.out;

public class ConstructorSift { public static void main(String... args) {

try { Class<?> cArg = Class.forName(args[1]);

Class<?> c = Class.forName(args[0]); Constructor[] allConstructors =

c.getDeclaredConstructors(); for (Constructor ctor : allConstructors)

{Class<?>[] pType =

ctor.getParameterTypes();for (int i = 0; i < pType.length; i+

+) { if (pType[i].equals(cArg)) {

out.format("%s%n", ctor.toGenericString());

Type[] gpType = ctor.getGenericParameterTypes();

for (int j = 0; j < gpType.length; j++) {

char ch = (pType[j].equals(cArg) ? '*' : ' ');

out.format("%7c%s[%d]: %s%n", ch,

"GenericParameterType", j, gpType[j]);

}break;

}}

}

// production code should handle this exception more gracefully

} catch (ClassNotFoundException x) { x.printStackTrace();}

}}

Method.getGenericParameterTypes() will consult the Signature Attribute in the class file if it's present. If the attribute isn't available, it falls back on Method.getParameterType() which was not changed by the introduction of generics. The other methods with name

59

Page 60: Java Ref Election

getGenericFoo() for some value of Foo in reflection are implemented similarly. The syntax for the returned values of Method.get*Types() is described in Class.getName().

Here is the output for all constructors in java.util.Formatter which have a Locale argument.

$ java ConstructorSift java.util.Formatter java.util.Localepublicjava.util.Formatter(java.io.OutputStream,java.lang.String,java.util.Locale)throws java.io.UnsupportedEncodingException GenericParameterType[0]: class java.io.OutputStream GenericParameterType[1]: class java.lang.String *GenericParameterType[2]: class java.util.Localepublic java.util.Formatter(java.lang.String,java.lang.String,java.util.Locale)throws java.io.FileNotFoundException,java.io.UnsupportedEncodingException GenericParameterType[0]: class java.lang.String GenericParameterType[1]: class java.lang.String *GenericParameterType[2]: class java.util.Localepublic java.util.Formatter(java.lang.Appendable,java.util.Locale) GenericParameterType[0]: interface java.lang.Appendable *GenericParameterType[1]: class java.util.Localepublic java.util.Formatter(java.util.Locale) *GenericParameterType[0]: class java.util.Localepublic java.util.Formatter(java.io.File,java.lang.String,java.util.Locale)throws java.io.FileNotFoundException,java.io.UnsupportedEncodingException GenericParameterType[0]: class java.io.File GenericParameterType[1]: class java.lang.String *GenericParameterType[2]: class java.util.Locale

60

Page 61: Java Ref Election

The next example output illustrates how to search for a parameter of type char[] in String.

$ java ConstructorSift java.lang.String "[C"java.lang.String(int,int,char[]) GenericParameterType[0]: int GenericParameterType[1]: int *GenericParameterType[2]: class [Cpublic java.lang.String(char[],int,int) *GenericParameterType[0]: class [C GenericParameterType[1]: int GenericParameterType[2]: intpublic java.lang.String(char[]) *GenericParameterType[0]: class [C

The syntax for expressing arrays of reference and primitive types acceptable to Class.forName() is described in Class.getName(). Note that the first listed constructor is package-private, not public. It is returned because the example code uses Class.getDeclaredConstructors() rather than Class.getConstructors(), which returns only public constructors.

This example shows that searching for arguments of variable arity (which have a variable number of parameters) requires use of array syntax:

$ java ConstructorSift java.lang.ProcessBuilder "[Ljava.lang.String;"public java.lang.ProcessBuilder(java.lang.String[]) *GenericParameterType[0]: class [Ljava.lang.String;

This is the actual declaration of the ProcessBuilder constructor in source code:

public ProcessBuilder(String... command)

The parameter is represented as a single-dimension array of type java.lang.String. This can be distinguished from a parameter that is explicitly an array of java.lang.String by invoking Constructor.isVarArgs().

The final example reports the output for a constructor which has been declared with a generic parameter type:

$ java ConstructorSift java.util.HashMap java.util.Mappublic java.util.HashMap(java.util.Map<? extends K, ? extends V>)

61

Page 62: Java Ref Election

*GenericParameterType[0]: java.util.Map<? extends K, ? extends V>

Exception types may be retrieved for constructors in a similar way as for methods. See the MethodSpy example described in Obtaining Method Type Information section for further details.

Retrieving and Parsing Constructor Modifiers

Because of the role of constructors in the language, fewer modifiers are meaningful than for methods:

Access modifiers: public, protected, and private Annotations

The ConstructorAccess example searches for constructors in a given class with the specified access modifier. It also displays whether the constructor is synthetic (compiler-generated) or of variable arity.

import java.lang.reflect.Constructor;import java.lang.reflect.Modifier;import java.lang.reflect.Type;import static java.lang.System.out;

public class ConstructorAccess { public static void main(String... args) {

try { Class<?> c = Class.forName(args[0]); Constructor[] allConstructors =

c.getDeclaredConstructors(); for (Constructor ctor : allConstructors)

{int searchMod =

modifierFromString(args[1]);int mods =

accessModifiers(ctor.getModifiers());if (searchMod == mods) { out.format("%s%n",

ctor.toGenericString()); out.format(" [ synthetic=%-5b

var_args=%-5b ]%n", ctor.isSynthetic(),

ctor.isVarArgs());}

}

// production code should handle this exception more gracefully

} catch (ClassNotFoundException x) {

62

Page 63: Java Ref Election

x.printStackTrace();}

}

private static int accessModifiers(int m) {return m & (Modifier.PUBLIC |

Modifier.PRIVATE | Modifier.PROTECTED); }

private static int modifierFromString(String s) {

if ("public".equals(s)) return Modifier.PUBLIC;

else if ("protected".equals(s)) return Modifier.PROTECTED;

else if ("private".equals(s)) return Modifier.PRIVATE;

else if ("package-private".equals(s)) return 0;

else return -1; }}

There is not an explicit Modifier constant which corresponds to "package-private" access, so it is necessary to check for the absence of all three access modifiers to identify a package-private constructor.

This output shows the private constructors in java.io.File:

$ java ConstructorAccess java.io.File privateprivate java.io.File(java.lang.String,int) [ synthetic=false var_args=false ]private java.io.File(java.lang.String,java.io.File) [ synthetic=false var_args=false ]

Synthetic constructors are rare; however the SyntheticConstructor example illustrates a typical situation where this may occur:

public class SyntheticConstructor { private SyntheticConstructor() {} class Inner {

// Compiler will generate a synthetic constructor since

// SyntheticConstructor() is private.Inner() { new SyntheticConstructor(); }

}}$ java ConstructorAccess SyntheticConstructor package-privateSyntheticConstructor(SyntheticConstructor$1) [ synthetic=true var_args=false ]

63

Page 64: Java Ref Election

Since the inner class's constructor references the private constructor of the enclosing class, the compiler must generate a package-private constructor. The parameter type SyntheticConstructor$1 is arbitrary and dependent on the compiler implementation. Code which depends on the presence of any synthetic or non-public class members may not be portable.

Constructors implement java.lang.reflect.AnnotatedElement, which provides methods to retrieve runtime annotations with java.lang.annotation.RetentionPolicy.RUNTIME. For an example of obtaining annotations see the Examining Class Modifiers and Types section.

Creating New Class Instances

There are two reflective methods for creating instances of classes: java.lang.reflect.Constructor.newInstance() and Class.newInstance(). The former is preferred and is thus used in these examples because:

Class.newInstance() can only invoke the zero-argument constructor, while Constructor.newInstance() may invoke any constructor, regardless of the number of parameters.

Class.newInstance() throws any exception thrown by the constructor, regardless of whether it is checked or unchecked. InvocationTargetException.

Class.newInstance() requires that the constructor be visible; Constructor.newInstance() may invoke private constructors under certain circumstances.

Sometimes it may be desirable to retrieve internal state from an object which is only set after construction. Consider a scenario where it is necessary to obtain the internal character set used by java.io.Console. (The Console character set is stored in an private field and is not necessarily the same as the Java virtual machine default character set returned by java.nio.charset.Charset.defaultCharset()). The ConsoleCharset example shows how this might be achieved:

import java.io.Console;import java.nio.charset.Charset;import java.lang.reflect.Constructor;import java.lang.reflect.Field;import java.lang.reflect.InvocationTargetException;import static java.lang.System.out;

public class ConsoleCharset { public static void main(String... args) {

64

Page 65: Java Ref Election

Constructor[] ctors = Console.class.getDeclaredConstructors();

Constructor ctor = null;for (int i = 0; i < ctors.length; i++) { ctor = ctors[i]; if

(ctor.getGenericParameterTypes().length == 0)break;

}

try { ctor.setAccessible(true);

Console c = (Console)ctor.newInstance(); Field f =

c.getClass().getDeclaredField("cs"); f.setAccessible(true); out.format("Console charset :

%s%n", f.get(c)); out.format("Charset.defaultCharset():

%s%n", Charset.defaultCharset());

// production code should handle these exceptions more gracefully

} catch (InstantiationException x) { x.printStackTrace();

} catch (InvocationTargetException x) { x.printStackTrace();

} catch (IllegalAccessException x) { x.printStackTrace();} catch (NoSuchFieldException x) { x.printStackTrace();}

}}

Note: Class.newInstance() will only succeed if the constructor is has zero arguments and is already accessible. Otherwise, it is necessary to use Constructor.newInstance() as in the above example.

Example output for a Unix system:

$ java ConsoleCharsetConsole charset : ISO-8859-1Charset.defaultCharset() : ISO-8859-1

Example output for a Windows system:

65

Page 66: Java Ref Election

C:\> java ConsoleCharsetConsole charset : IBM437Charset.defaultCharset() : windows-1252

Another common application of Constructor.newInstance() is to invoke constructors which take arguments. The RestoreAliases example finds a specific single-argument constructor and invokes it:

import java.lang.reflect.Constructor;import java.lang.reflect.Field;import java.lang.reflect.InvocationTargetException;import java.util.HashMap;import java.util.Map;import java.util.Set;import static java.lang.System.out;

class EmailAliases { private Set<String> aliases; private EmailAliases(HashMap<String, String> h) {

aliases = h.keySet(); }

public void printKeys() {out.format("Mail keys:%n");for (String k : aliases) out.format(" %s%n", k);

}}

public class RestoreAliases {

private static Map<String, String> defaultAliases = new HashMap<String, String>(); static {

defaultAliases.put("Duke", "duke@i-love-java");

defaultAliases.put("Fang", "fang@evil-jealous-twin"); }

public static void main(String... args) {try { Constructor ctor =

EmailAliases.class.getDeclaredConstructor(HashMap.class);

ctor.setAccessible(true); EmailAliases email =

(EmailAliases)ctor.newInstance(defaultAliases); email.printKeys();

// production code should handle these exceptions more gracefully

66

Page 67: Java Ref Election

} catch (InstantiationException x) { x.printStackTrace();} catch (IllegalAccessException x) { x.printStackTrace();} catch (InvocationTargetException x) { x.printStackTrace();} catch (NoSuchMethodException x) { x.printStackTrace();}

}}

This example uses Class.getDeclaredConstructor() to find the constructor with a single argument of type java.util.HashMap. Note that it is sufficient to pass HashMap.class since the parameter to any get*Constructor() method requires a class only for type purposes. Due to type erasure, the following expression evaluates to true:

HashMap.class == defaultAliases.getClass()

The example then creates a new instance of the class using this constructor with Constructor.newInstance().

$ java RestoreAliasesMail keys: Duke Fang

Troubleshooting

The following problems are sometimes encountered by developers when trying to invoke constructors via reflection.

InstantiationException Due to Missing Zero-Argument Constructor

The ConstructorTrouble example illustrates what happens when code attempts to create a new instance of a class using Class.newInstance() and there is no accessible zero-argument constructor:

public class ConstructorTrouble { private ConstructorTrouble(int i) {}

public static void main(String... args){try { Class<?> c =

Class.forName("ConstructorTrouble");

67

Page 68: Java Ref Election

Object o = c.newInstance(); // InstantiationException

// production code should handle these exceptions more gracefully

} catch (ClassNotFoundException x) { x.printStackTrace();} catch (InstantiationException x) { x.printStackTrace();} catch (IllegalAccessException x) { x.printStackTrace();}

}}$ java ConstructorTroublejava.lang.InstantiationException: ConstructorTrouble at java.lang.Class.newInstance0(Class.java:340) at java.lang.Class.newInstance(Class.java:308) at ConstructorTrouble.main(ConstructorTrouble.java:7)

Tip: There a number of different reasons an InstantiationException can occur. In this case, the problem is that the presence of the constructor with an int argument prevents the compiler from generating the default (or zero-argument) constructor and there is no explicit zero-argument constructor in the code. Remember that Class.newInstance() behaves very much like the new keyword and will fail whenever new would fail.

Class.newInstance() Throws Unexpected Exception

The ConstructorTroubleToo example shows an unresolvable problem in Class.newInstance(). Namely, it propagates any exception — checked or unchecked — thrown by the constructor.

import java.lang.reflect.InvocationTargetException;import static java.lang.System.err;

public class ConstructorTroubleToo { public ConstructorTroubleToo() {

68

Page 69: Java Ref Election

throw new RuntimeException("exception in constructor"); }

public static void main(String... args) {try { Class<?> c =

Class.forName("ConstructorTroubleToo"); // Method propagetes any exception

thrown by the constructor // (including checked exceptions). if (args.length > 0 &&

args[0].equals("class")) {Object o = c.newInstance();

} else {Object o =

c.getConstructor().newInstance(); }

// production code should handle these exceptions more gracefully

} catch (ClassNotFoundException x) { x.printStackTrace();} catch (InstantiationException x) { x.printStackTrace();} catch (IllegalAccessException x) { x.printStackTrace();} catch (NoSuchMethodException x) { x.printStackTrace();} catch (InvocationTargetException x) { x.printStackTrace(); err.format("%n%nCaught exception: %s%n",

x.getCause());}

}}$ java ConstructorTroubleToo classException in thread "main" java.lang.RuntimeException: exception in constructor at ConstructorTroubleToo.<init>(ConstructorTroubleToo.java:6) at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:39) at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27) at java.lang.reflect.Constructor.newInstance(Constructor.java:513)

69

Page 70: Java Ref Election

at java.lang.Class.newInstance0(Class.java:355) at java.lang.Class.newInstance(Class.java:308) at ConstructorTroubleToo.main(ConstructorTroubleToo.java:15)

This situation is unique to reflection. Normally, it is impossible to write code which ignores a checked exception because it would not compile. It is possible to wrap any exception thrown by a constructor by using Constructor.newInstance() rather than Class.newInstance().

$ java ConstructorTroubleToojava.lang.reflect.InvocationTargetException at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:39) at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27) at java.lang.reflect.Constructor.newInstance(Constructor.java:513) at ConstructorTroubleToo.main(ConstructorTroubleToo.java:17)Caused by: java.lang.RuntimeException: exception in constructor at ConstructorTroubleToo.<init>(ConstructorTroubleToo.java:6) ... 5 more

Caught exception: java.lang.RuntimeException: exception in constructor

If an InvocationTargetException is thrown, the method was invoked. Diagnosis of the problem would be the same as if the constructor was called directly and threw the exception that is retrieved by InvocationTargetException.getCause(). This exception does not indicate a problem with the reflection package or its usage.

Tip: It is preferable to use Constructor.newInstance() over Class.newInstance() because the former API

70

Page 71: Java Ref Election

permits examination and handling of arbitrary exceptions thrown by constructors.

Problems Locating or Invoking the Correct Constructor

The ConstructorTroubleAgain class illustrates various ways in which incorrect code can fail to locate or invoke the expected constructor.

import java.lang.reflect.InvocationTargetException;import static java.lang.System.out;

public class ConstructorTroubleAgain { public ConstructorTroubleAgain() {}

public ConstructorTroubleAgain(Integer i) {}

public ConstructorTroubleAgain(Object o) {out.format("Constructor passed Object%n");

}

public ConstructorTroubleAgain(String s) {out.format("Constructor passed String%n");

}

public static void main(String... args){String argType = (args.length == 0 ? "" :

args[0]);try { Class<?> c =

Class.forName("ConstructorTroubleAgain"); if ("".equals(argType)) {

// IllegalArgumentException: wrong number of arguments

Object o = c.getConstructor().newInstance("foo");

} else if ("int".equals(argType)) {// NoSuchMethodException - looking

for int, have IntegerObject o =

c.getConstructor(int.class); } else if ("Object".equals(argType)) {

// newInstance() does not perform method resolution

Object o = c.getConstructor(Object.class).newInstance("foo");

} else {assert false;

}

71

Page 72: Java Ref Election

// production code should handle these exceptions more gracefully

} catch (ClassNotFoundException x) { x.printStackTrace();} catch (NoSuchMethodException x) { x.printStackTrace();} catch (InvocationTargetException x) { x.printStackTrace();} catch (InstantiationException x) { x.printStackTrace();} catch (IllegalAccessException x) { x.printStackTrace();}

}}$ java ConstructorTroubleAgainException in thread "main" java.lang.IllegalArgumentException: wrong number of arguments at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:39) at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27) at java.lang.reflect.Constructor.newInstance(Constructor.java:513) at ConstructorTroubleAgain.main(ConstructorTroubleAgain.java:23)

An IllegalArgumentException is thrown because the zero-argument constructor was requested and an attempt was made to pass an argument. The same exception would be thrown if the constructor was passed an argument of the wrong type.

$ java ConstructorTroubleAgain intjava.lang.NoSuchMethodException: ConstructorTroubleAgain.<init>(int) at java.lang.Class.getConstructor0(Class.java:2706) at java.lang.Class.getConstructor(Class.java:1657) at ConstructorTroubleAgain.main(ConstructorTroubleAgain.java:26)

This exception may occur if the developer mistakenly believes that reflection will autobox or unbox types. Boxing (conversion of a primitive

72

Page 73: Java Ref Election

to a reference type) occurs only during compilation. There is no opportunity in reflection for this operation to occur, so the specific type must be used when locating a constructor.

$ java ConstructorTroubleAgain ObjectConstructor passed Object

Here, it might be expected that the constructor taking a String argument would be invoked since newInstance() was invoked with the more specific String type. However it is too late! The constructor which was found is already the constructor with an Object argument. newInstance() makes no attempt to do method resolution; it simply operates on the existing constructor object.

Tip: An important difference between new and Constructor.newInstance() is that new performs method argument type checking, boxing, and method resolution. None of these occur in reflection, where explicit choices must be made.

IllegalAccessException When Attempting to Invoke an Inaccessible Constructor

An IllegalAccessException may be thrown if an attempt is made to invoke a private or otherwise inaccessible constructor. The ConstructorTroubleAccess example illustrates the resulting stack trace.

import java.lang.reflect.Constructor;import java.lang.reflect.InvocationTargetException;

class Deny { private Deny() {

System.out.format("Deny constructor%n"); }}

public class ConstructorTroubleAccess { public static void main(String... args) {

try { Constructor c =

Deny.class.getDeclaredConstructor();// c.setAccessible(true); // solution

c.newInstance();

73

Page 74: Java Ref Election

// production code should handle these exceptions more gracefully

} catch (InvocationTargetException x) { x.printStackTrace();} catch (NoSuchMethodException x) { x.printStackTrace();} catch (InstantiationException x) { x.printStackTrace();} catch (IllegalAccessException x) { x.printStackTrace();}

}}$ java ConstructorTroubleAccessjava.lang.IllegalAccessException: Class ConstructorTroubleAccess can not access a member of class Deny with modifiers "private" at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:65) at java.lang.reflect.Constructor.newInstance(Constructor.java:505) at ConstructorTroubleAccess.main(ConstructorTroubleAccess.java:15)

Tip: An access restriction exists which prevents reflective invocation of constructors which normally would not be accessible via direct invocation. (This includes---but is not limited to---private constructors in a separate class and public constructors in a separate private class.) However, Constructor is declared to extend AccessibleObject which provides the ability to suppress this check via AccessibleObject.setAccessible().

Lesson: Arrays and Enumerated Types

From the Java virtual machine's perspective, arrays and enumerated types (or enums) are just classes. Many of the methods in Class may be used on them. Reflection provides a few specific APIs for arrays and enums. This lesson uses a series of code samples to describe how to distinguish each of these objects from other classes and operate on them. Various errors are also be examined.

Arrays

74

Page 75: Java Ref Election

Arrays have a component type and a length (which is not part of the type). Arrays may be maniuplated either in their entirety or component by component. Reflection provides the java.lang.reflect.Array class for the latter purpose.

Identifying Array Types describes how to determine if a class member is a field of array type

Creating New Arrays illustrates how to create new instances of arrays with simple and complex component types

Getting and Setting Arrays and Their Components shows how to access fields of type array and individually access array elements

Troubleshooting covers common errors and programming misconceptions

Enumerated Types

Enums are treated very much like ordinary classes in reflection code. Class.isEnum() tells whether a Class represents and enum. Class.getEnumConstants() retrieves the enum constants defined in an enum. java.lang.reflect.Field.isEnumConstant() indicates whether a field is an enumerated type.

Examining Enums illustrates how to retrieve an enum's constants and any other fields, constructors, and methods

Getting and Setting Fields with Enum Types shows how to set and get fields with an enum constant value

Troubleshooting describes common errors associated with enums

Arrays

An array is an object of reference type which contains a fixed number of components of the same type; the length of an array is immutable. Creating an instance of an array requires knowledge of the length and component type. Each component may be a primitive type (e.g. byte, int, or double), a reference type (e.g. String, Object, or java.nio.CharBuffer), or an array. Multi-dimensional arrays are really just arrays which contain components of array type.

Arrays are implemented in the Java virtual machine. The only methods on arrays are those inherited from Object. The length of an array is not part of its type; arrays have a length field which is accessible via java.lang.reflect.Array.getLength().

75

Page 76: Java Ref Election

Reflection provides methods for accessing array types and array component types, creating new arrays, and retrieving and setting array component values. The following sections include examples of common operations on arrays:

Identifying Array Types describes how to determine if a class member is a field of array type

Creating New Arrays illustrates how to create new instances of arrays with simple and complex component types

Getting and Setting Arrays and Their Components shows how to access fields of type array and individually access array elements

Troubleshooting covers common errors and programming misconceptions

All of these operations are supported via static methods in java.lang.reflect.Array.

Identifying Array Types

Array types may be identified by invoking Class.isArray(). To obtain a Class use one of the methods described in Retrieving Class Objects section of this trail.

The ArrayFind example identifies the fields in the named class that are of array type and reports the component type for each of them.

import java.lang.reflect.Field;import java.lang.reflect.Type;import static java.lang.System.out;

public class ArrayFind { public static void main(String... args) {

boolean found = false; try {

Class<?> cls = Class.forName(args[0]); Field[] flds = cls.getDeclaredFields(); for (Field f : flds) {

Class<?> c = f.getType();if (c.isArray()) { found = true; out.format("%s%n"

+ " Field: %s%n"

+ " Type: %s%n"

+ " Component Type: %s%n",

76

Page 77: Java Ref Election

f, f.getName(), c, c.getComponentType());

} } if (!found) {

out.format("No array fields%n"); }

// production code should handle this exception more gracefully } catch (ClassNotFoundException x) {

x.printStackTrace();}

}}

The syntax for the returned value of Class.get*Type() is described in Class.getName(). The number of '[' characters at the beginning of the type name indicates the number of dimensions (i.e. depth of nesting) of the array.

Samples of the output follows. User input is in italics. An array of primitive type byte:

$ java ArrayFind java.nio.ByteBufferfinal byte[] java.nio.ByteBuffer.hb Field: hb Type: class [B Component Type: byte

An array of reference type StackTraceElement:

$ java ArrayFind java.lang.Throwableprivate java.lang.StackTraceElement[] java.lang.Throwable.stackTrace Field: stackTrace Type: class [Ljava.lang.StackTraceElement; Component Type: class java.lang.StackTraceElement

predefined is a one-dimensional array of reference type java.awt.Cursor and cursorProperties is a two-dimensional array of reference type String:

$ java ArrayFind java.awt.Cursorprotected static java.awt.Cursor[] java.awt.Cursor.predefined Field: predefined Type: class [Ljava.awt.Cursor; Component Type: class java.awt.Cursorstatic final java.lang.String[][] java.awt.Cursor.cursorProperties

77

Page 78: Java Ref Election

Field: cursorProperties Type: class [[Ljava.lang.String; Component Type: class [Ljava.lang.String;

Creating New Arrays

Just as in non-reflective code, reflection supports the ability to dynamically create arrays of arbitrary type and dimensions via java.lang.reflect.Array.newInstance(). Consider ArrayCreator, a basic interpreter capable of dynamically creating arrays. The syntax that will be parsed is as follows:

fully_qualified_class_name variable_name[] = { val1, val2, val3, ... }

Assume that the fully_qualified_class_name represents a class that has a constructor with a single String argument. The dimensions of the array are determined by the number of values provided. The following example will construct an instance of an array of fully_qualified_class_name and populate its values with instances given by val1, val2, etc. (This example assumes familiarity with Class.getConstructor() and java.lang.reflect.Constructor.newInstance(). For a discussion of the reflection APIs for Constructor see the Creating New Class Instances section of this trail.)

import java.lang.reflect.Array;import java.lang.reflect.Constructor;import java.lang.reflect.InvocationTargetException;import java.util.regex.Pattern;import java.util.regex.Matcher;import java.util.Arrays;import static java.lang.System.out;

public class ArrayCreator { private static String s = "java.math.BigInteger bi[] = { 123, 234, 345 }"; private static Pattern p = Pattern.compile("^\\s*(\\S+)\\s*\\w+\\[\\].*\\{\\s*([^}]+)\\s*\\}");

public static void main(String... args) { Matcher m = p.matcher(s);

if (m.find()) { String cName = m.group(1);

78

Page 79: Java Ref Election

String[] cVals = m.group(2).split("[\\s,]+"); int n = cVals.length;

try { Class<?> c = Class.forName(cName); Object o = Array.newInstance(c, n); for (int i = 0; i < n; i++) { String v = cVals[i]; Constructor ctor = c.getConstructor(String.class); Object val = ctor.newInstance(v); Array.set(o, i, val); }

Object[] oo = (Object[])o; out.format("%s[] = %s%n", cName, Arrays.toString(oo));

// production code should handle these exceptions more gracefully } catch (ClassNotFoundException x) { x.printStackTrace(); } catch (NoSuchMethodException x) { x.printStackTrace(); } catch (IllegalAccessException x) { x.printStackTrace(); } catch (InstantiationException x) { x.printStackTrace(); } catch (InvocationTargetException x) { x.printStackTrace(); } } }}$ java ArrayCreatorjava.math.BigInteger [] = [123, 234, 345]

The above example shows one case where it may be desirable to create an array via reflection; namely if the component type is not known until runtime. In this case, the code uses Class.forName() to get a class for the desired component type and then calls a specific constructor to initialize each component of the array before setting the corresponding array value.

Getting and Setting Arrays and Their Components

Just as in non-reflective code, an array field may be set or retrieved in its entirety or component by component. To set the entire array at once, use

79

Page 80: Java Ref Election

java.lang.reflect.Field.set(Object obj, Object value). To retrieve the entire array, use Field.get(Object). Individual components can be set or retrieved using methods in java.lang.reflect.Array.

Array provides methods of the form setFoo() and getFoo() for setting and getting components of any primitive type. For example, the component of an int array may be set with Array.setInt(Object array, int index, int value) and may be retrieved with Array.getInt(Object array, int index).

These methods support automatic widening of data types. Therefore, Array.getShort() may be used to set the values of an int array since a 16-bit short may be widened to a 32-bit int without loss of data; on the other hand, invoking Array.setLong() on an array of int will cause an IllegalArgumentException to be thrown because a 64-bit long can not be narrowed to for storage in a 32-bit int with out loss of information. This is true regardless of whether the actual values being passed could be accurately represented in the target data type. The Java Language Specification, Third Edition, sections 5.1.2 and 5.1.3 contains a complete discussion of widening and narrowing conversions.

The components of arrays of reference types (including arrays of arrays) are set and retrieved using Array.set(Object array, int index, int value)and Array.get(Object array, int index).

Setting a Field of Type Array

The GrowBufferedReader example illustrates how to replace the value of a field of type array. In this case, the code replaces the backing array for a java.io.BufferedReader with a larger one. (This assumes that the creation of the original BufferedReader is in code that is not modifiable; otherwise, it would be trivial to simply use the alternate constructor BufferedReader(java.io.Reader in, int size) which accepts an input buffer size.)

import java.io.BufferedReader;import java.io.CharArrayReader;import java.io.FileNotFoundException;import java.io.IOException;import java.lang.reflect.Field;import java.util.Arrays;import static java.lang.System.out;

public class GrowBufferedReader { private static final int srcBufSize = 10 * 1024;

80

Page 81: Java Ref Election

private static char[] src = new char[srcBufSize]; static {

src[srcBufSize - 1] = 'x'; } private static CharArrayReader car = new CharArrayReader(src);

public static void main(String... args) {try { BufferedReader br = new

BufferedReader(car);

Class<?> c = br.getClass(); Field f = c.getDeclaredField("cb");

// cb is a private field f.setAccessible(true); char[] cbVal =

char[].class.cast(f.get(br));

char[] newVal = Arrays.copyOf(cbVal, cbVal.length * 2);

if (args.length > 0 && args[0].equals("grow"))

f.set(br, newVal);

for (int i = 0; i < srcBufSize; i++)br.read();

// see if the new backing array is being used

if (newVal[srcBufSize - 1] == src[srcBufSize - 1])

out.format("Using new backing array, size=%d%n", newVal.length);

elseout.format("Using original backing

array, size=%d%n", cbVal.length);

// production code should handle these exceptions more gracefully

} catch (FileNotFoundException x) { x.printStackTrace();} catch (NoSuchFieldException x) { x.printStackTrace();} catch (IllegalAccessException x) { x.printStackTrace();} catch (IOException x) { x.printStackTrace();}

}}$ java GrowBufferedReader growUsing new backing array, size=16384$ java GrowBufferedReaderUsing original backing array, size=8192

81

Page 82: Java Ref Election

Note that the above example makes use of the array utility method java.util.Arrays.copyOf). java.util.Arrays contains many methods which are convenient when operating on arrays.

Accessing Elements of a Multidimensional Array

Multi-dimensional arrays are simply nested arrays. A two-dimensional array is an array of arrays. A three-dimensional array is an array of two-dimensional arrays, and so on. The CreateMatrix example illustrates how to create and initialize a multi-dimensional array using reflection.

import java.lang.reflect.Array;import static java.lang.System.out;

public class CreateMatrix { public static void main(String... args) { Object matrix = Array.newInstance(int.class, 2, 2); Object row0 = Array.get(matrix, 0); Object row1 = Array.get(matrix, 1);

Array.setInt(row0, 0, 1); Array.setInt(row0, 1, 2); Array.setInt(row1, 0, 3); Array.setInt(row1, 1, 4);

for (int i = 0; i < 2; i++) for (int j = 0; j < 2; j++) out.format("matrix[%d][%d] = %d%n", i, j, ((int[][])matrix)[i][j]); }}$ java CreateMatrixmatrix[0][0] = 1matrix[0][1] = 2matrix[1][0] = 3matrix[1][1] = 4

The same result could be obtained by using the following code fragment:

Object matrix = Array.newInstance(int.class, 2);Object row0 = Array.newInstance(int.class, 2);Object row1 = Array.newInstance(int.class, 2);

Array.setInt(row0, 0, 1);Array.setInt(row0, 1, 2);Array.setInt(row1, 0, 3);Array.setInt(row1, 1, 4);

Array.set(matrix, 0, row0);Array.set(matrix, 1, row1);

82

Page 83: Java Ref Election

The variable argument Array.newInstance(Class<?> componentType, int... dimensions) provides a convenient way to create multi-dimensional arrays, but the components still need to initialized using the principle that that multi-dimensional arrays are nested arrays. (Reflection does not provide multiple indexed get/set methods for this purpose.)

Troubleshooting

The following examples show typical errors which may occur when operating on arrays.

IllegalArgumentException due to Inconvertible Types

The ArrayTroubleAgain example will generate an IllegalArgumentException. Array.setInt() is invoked to set a component that is of the reference type Integer with a value of primitive type int. In the non-reflection equivalent ary[0] = 1, the compiler would convert (or box) the value 1 to a reference type as new Integer(1) so that its type checking will accept the statement. When using reflection, type checking only occurs at runtime so there is no opportunity to box the value.

import java.lang.reflect.Array;import static java.lang.System.err;

public class ArrayTroubleAgain { public static void main(String... args) {

Integer[] ary = new Integer[2];try { Array.setInt(ary, 0, 1); //

IllegalArgumentException

// production code should handle these exceptions more gracefully

} catch (IllegalArgumentException x) { err.format("Unable to box%n");} catch (ArrayIndexOutOfBoundsException x) { x.printStackTrace();}

}}$ java ArrayTroubleAgainUnable to box

To eliminate this exception, the problematic line should be replaced by the following invocation of Array.set(Object array, int index, Object value):

Array.set(ary, 0, new Integer(1));

83

Page 84: Java Ref Election

Tip: When using reflection to set or get an array component, the compiler does not have an opportunity to perform boxing. It can only convert types that are related as described by the specification for Class.isAssignableFrom(). The example is expected to fail because isAssignableFrom() will return false in this test which can be used programmatically to verify whether a particular conversion is possible:

Integer.class.isAssignableFrom(int.class) == false

Similarly, automatic conversion from primitive to reference type is also impossible in reflection.

int.class.isAssignableFrom(Integer.class) == false

ArrayIndexOutOfBoundsException for Empty Arrays

The ArrayTrouble example illustrates an error which will occur if an attempt is made to access the elements of an array of zero length:

import java.lang.reflect.Array;import static java.lang.System.out;

public class ArrayTrouble { public static void main(String... args) { Object o = Array.newInstance(int.class, 0); int[] i = (int[])o; int[] j = new int[0]; out.format("i.length = %d, j.length = %d, args.length = %d%n", i.length, j.length, args.length); Array.getInt(o, 0); // ArrayIndexOutOfBoundsException }}$ java ArrayTroublei.length = 0, j.length = 0, args.length = 0Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException at java.lang.reflect.Array.getInt(Native Method)

84

Page 85: Java Ref Election

at ArrayTrouble.main(ArrayTrouble.java:11)

Tip: It is possible to have arrays with no elements (empty arrays). There are only a few cases in common code where they are seen but they can occur in reflection inadvertently. Of course, it is not possible to set/get the values of an empty array because an ArrayIndexOutOfBoundsException will be thrown.

IllegalArgumentException if Narrowing is Attempted

The ArrayTroubleToo example contains code which fails because it attempts perform an operation which could potentially lose data:

import java.lang.reflect.Array;import static java.lang.System.out;

public class ArrayTroubleToo { public static void main(String... args) { Object o = new int[2]; Array.setShort(o, 0, (short)2); // widening, succeeds Array.setLong(o, 1, 2L); // narrowing, fails }}$ java ArrayTroubleTooException in thread "main" java.lang.IllegalArgumentException: argument type mismatch at java.lang.reflect.Array.setLong(Native Method) at ArrayTroubleToo.main(ArrayTroubleToo.java:9)

Tip:  The Array.set*() and Array.get*() methods will perform automatic widening conversion but will throw an IllegalArgumentException if a narrowing conversion is attempted. For complete discussion of widening and narrowing conversions, see The Java Language Specification, Third Edition, sections 5.1.2 and 5.1.3 respectively.

85

Page 86: Java Ref Election

Enumerated Types

An enum is a language construct that is used to define type-safe enumerations which can be used when a fixed set of named values is desired. All enums implicitly extend java.lang.Enum. Enums may contain one or more enum constants, which define unique instances of the enum type. An enum declaration defines an enum type which is very similar to a class in that it may have members such as fields, methods, and constructors (with some restrictions).

Since enums are classes, reflection has no need to define an explicit java.lang.reflect.Enum class. The only Reflection APIs that are specific to enums are Class.isEnum(), Class.getEnumConstants(), and java.lang.reflect.Field.isEnumConstant(). Most reflective operations involving enums are the same as any other class or member. For example, enum constants are implemented as public static final fields on the enum. The following sections show how to use Class and java.lang.reflect.Field with enums.

Examining Enums illustrates how to retrieve an enum's constants and any other fields, constructors, and methods

Getting and Setting Fields with Enum Types shows how to set and get fields with an enum constant value

Troubleshooting describes common errors associated with enums

For an introduction to enums, see the Enum Types section of the Java Tutorial Trail Learning the Java Language.

Examining Enums

Reflection provides three enum-specific APIs:

Class.isEnum() Indicates whether this class represents an enum type Class.getEnumConstants() Retrieves the list of enum constants defined by the enum in the order they're declared java.lang.reflect.Field.isEnumConstant() Indicates whether this field represents an element of an enumerated type

Sometimes it is necessary to dynamically retrieve the list of enum constants; in non-reflective code this is accomplished by invoking the implicitly declared static method values() on the enum. If an instance of an enum type is not available the only way to get a list of the possible

86

Page 87: Java Ref Election

values is to invoke Class.getEnumConstants() since it is impossible to instantiate an enum type.

Given a fully qualified name, the EnumConstants example shows how to retrieve an ordered list of constants in an enum using Class.getEnumConstants().

import java.util.Arrays;import static java.lang.System.out;

enum Eon { HADEAN, ARCHAEAN, PROTEROZOIC, PHANEROZOIC }

public class EnumConstants { public static void main(String... args) {

try { Class<?> c = (args.length == 0 ?

Eon.class : Class.forName(args[0])); out.format("Enum name: %s%nEnum

constants: %s%n", c.getName(),

Arrays.asList(c.getEnumConstants())); if (c == Eon.class)

out.format(" Eon.values(): %s%n",

Arrays.asList(Eon.values()));

// production code should handle this exception more gracefully

} catch (ClassNotFoundException x) { x.printStackTrace();}

}}

Samples of the output follows. User input is in italics.

$ java EnumConstants java.lang.annotation.RetentionPolicyEnum name: java.lang.annotation.RetentionPolicyEnum constants: [SOURCE, CLASS, RUNTIME]$ java EnumConstants java.util.concurrent.TimeUnitEnum name: java.util.concurrent.TimeUnitEnum constants: [NANOSECONDS, MICROSECONDS, MILLISECONDS, SECONDS, MINUTES, HOURS, DAYS]

This example also shows that value returned by Class.getEnumConstants() is identical to the value returned by invoking values() on an enum type.

$ java EnumConstants

87

Page 88: Java Ref Election

Enum name: EonEnum constants: [HADEAN, ARCHAEAN, PROTEROZOIC, PHANEROZOIC] Eon.values(): [HADEAN, ARCHAEAN, PROTEROZOIC, PHANEROZOIC]

Since enums are classes, other information may be obtained using the same Reflection APIs described in the Fields, Methods, and Constructors sections of this trail. The EnumSpy code illustrates how to use these APIs to get additional information about the enum's declaration. The example uses Class.isEnum() to restrict the set of classes examined. It also uses Field.isEnumConstant() to distinguish enum constants from other fields in the enum declaration (not all fields are enum constants).

import java.lang.reflect.Constructor;import java.lang.reflect.Field;import java.lang.reflect.Method;import java.lang.reflect.Member;import java.util.List;import java.util.ArrayList;import static java.lang.System.out;

public class EnumSpy { private static final String fmt = " %11s: %s %s%n";

public static void main(String... args) {try { Class<?> c = Class.forName(args[0]); if (!c.isEnum()) {

out.format("%s is not an enum type%n", c);

return; } out.format("Class: %s%n", c);

Field[] flds = c.getDeclaredFields(); List<Field> cst = new

ArrayList<Field>(); // enum constants List<Field> mbr = new

ArrayList<Field>(); // member fields for (Field f : flds) {

if (f.isEnumConstant()) cst.add(f);else mbr.add(f);

} if (!cst.isEmpty())

print(cst, "Constant"); if (!mbr.isEmpty())

print(mbr, "Field");

Constructor[] ctors = c.getDeclaredConstructors();

88

Page 89: Java Ref Election

for (Constructor ctor : ctors) {out.format(fmt, "Constructor",

ctor.toGenericString(), synthetic(ctor));

}

Method[] mths = c.getDeclaredMethods(); for (Method m : mths) {

out.format(fmt, "Method", m.toGenericString(),

synthetic(m)); }

// production code should handle this exception more gracefully

} catch (ClassNotFoundException x) { x.printStackTrace();}

}

private static void print(List<Field> lst, String s) {

for (Field f : lst) { out.format(fmt, s, f.toGenericString(), synthetic(f));

} }

private static String synthetic(Member m) {return (m.isSynthetic() ? "[ synthetic ]" :

""); }}$ java EnumSpy java.lang.annotation.RetentionPolicyClass: class java.lang.annotation.RetentionPolicy Constant: public static final java.lang.annotation.RetentionPolicy java.lang.annotation.RetentionPolicy.SOURCE Constant: public static final java.lang.annotation.RetentionPolicy java.lang.annotation.RetentionPolicy.CLASS Constant: public static final java.lang.annotation.RetentionPolicy java.lang.annotation.RetentionPolicy.RUNTIME Field: private static final java.lang.annotation.RetentionPolicy[] java.lang.annotation.RetentionPolicy. [ synthetic ] Constructor: private java.lang.annotation.RetentionPolicy() Method: public static java.lang.annotation.RetentionPolicy java.lang.annotation.RetentionPolicy.valueOf(java.lang.String)

89

Page 90: Java Ref Election

Method: public static java.lang.annotation.RetentionPolicy[] java.lang.annotation.RetentionPolicy.values()

The output shows that declaration of java.lang.annotation.RetentionPolicy only contains the three enum constants. The enum constants are exposed as public static final fields. The field, constructor, and methods are compiler generated. The $VALUES field is related to the implementation of the values() method.

Note: For various reasons, including support for evolution of the enum type, the declaration order of enum constants is important. Class.getFields() and Class.getDeclaredFields() do not make any guarantee that the order of the returned values matches the order in the declaring source code. If ordering is required by an application, use Class.getEnumConstants().

The output for java.util.concurrent.TimeUnit shows that much more complicated enums are possible. This class includes several methods as well as additional fields declared static final which are not enum constants.

$ java EnumSpy java.util.concurrent.TimeUnitClass: class java.util.concurrent.TimeUnit Constant: public static final java.util.concurrent.TimeUnit java.util.concurrent.TimeUnit.NANOSECONDS Constant: public static final java.util.concurrent.TimeUnit java.util.concurrent.TimeUnit.MICROSECONDS Constant: public static final java.util.concurrent.TimeUnit java.util.concurrent.TimeUnit.MILLISECONDS Constant: public static final java.util.concurrent.TimeUnit java.util.concurrent.TimeUnit.SECONDS Constant: public static final java.util.concurrent.TimeUnit java.util.concurrent.TimeUnit.MINUTES Constant: public static final java.util.concurrent.TimeUnit java.util.concurrent.TimeUnit.HOURS Constant: public static final java.util.concurrent.TimeUnit java.util.concurrent.TimeUnit.DAYS

90

Page 91: Java Ref Election

Field: static final long java.util.concurrent.TimeUnit.C0 Field: static final long java.util.concurrent.TimeUnit.C1 Field: static final long java.util.concurrent.TimeUnit.C2 Field: static final long java.util.concurrent.TimeUnit.C3 Field: static final long java.util.concurrent.TimeUnit.C4 Field: static final long java.util.concurrent.TimeUnit.C5 Field: static final long java.util.concurrent.TimeUnit.C6 Field: static final long java.util.concurrent.TimeUnit.MAX Field: private static final java.util.concurrent.TimeUnit[] java.util.concurrent.TimeUnit. [ synthetic ] Constructor: private java.util.concurrent.TimeUnit() Constructor: java.util.concurrent.TimeUnit(java.lang.String,int,java.util.concurrent.TimeUnit) [ synthetic ] Method: public static java.util.concurrent.TimeUnit java.util.concurrent.TimeUnit.valueOf(java.lang.String) Method: public static java.util.concurrent.TimeUnit[] java.util.concurrent.TimeUnit.values() Method: public void java.util.concurrent.TimeUnit.sleep(long) throws java.lang.InterruptedException Method: public long java.util.concurrent.TimeUnit.toNanos(long) Method: public long java.util.concurrent.TimeUnit.convert(long,java.util.concurrent.TimeUnit) Method: abstract int java.util.concurrent.TimeUnit.excessNanos(long,long) Method: public void java.util.concurrent.TimeUnit.timedJoin(java.lang.Thread,long) throws java.lang.InterruptedException Method: public void java.util.concurrent.TimeUnit.timedWait(java.lang.Object,long) throws java.lang.InterruptedException Method: public long java.util.concurrent.TimeUnit.toDays(long) Method: public long java.util.concurrent.TimeUnit.toHours(long) Method: public long java.util.concurrent.TimeUnit.toMicros(long)

91

Page 92: Java Ref Election

Method: public long java.util.concurrent.TimeUnit.toMillis(long) Method: public long java.util.concurrent.TimeUnit.toMinutes(long) Method: public long java.util.concurrent.TimeUnit.toSeconds(long) Method: static long java.util.concurrent.TimeUnit.x(long,long,long)

Getting and Setting Fields with Enum Types

Fields which store enums are set and retrieved as any other reference type, using Field.set() and Field.get(). For more information on accessing fields, see the Fields section of this trail.

Consider application which needs to dynamically modify the trace level in a server application which normally does not allow this change during runtime. Assume the instance of the server object is available. The SetTrace example shows how code can translate the String representation of an enum into an enum type and retrieve and set the value of a field storing an enum.

import java.lang.reflect.Field;import static java.lang.System.out;

enum TraceLevel { OFF, LOW, MEDIUM, HIGH, DEBUG }

class MyServer { private TraceLevel level = TraceLevel.OFF;}

public class SetTrace { public static void main(String... args) {

TraceLevel newLevel = TraceLevel.valueOf(args[0]);

try { MyServer svr = new MyServer(); Class<?> c = svr.getClass(); Field f = c.getDeclaredField("level"); f.setAccessible(true); TraceLevel oldLevel =

(TraceLevel)f.get(svr); out.format("Original trace level: %s

%n", oldLevel);

if (oldLevel != newLevel) { f.set(svr, newLevel);

92

Page 93: Java Ref Election

out.format(" New trace level: %s%n", f.get(svr));

}

// production code should handle these exceptions more gracefully

} catch (IllegalArgumentException x) { x.printStackTrace();} catch (IllegalAccessException x) { x.printStackTrace();} catch (NoSuchFieldException x) { x.printStackTrace();}

}}

Since the enum constants are singletons, the == and != operators may be used to compare enum constants of the same type.

$ java SetTrace OFFOriginal trace level: OFF$ java SetTrace DEBUGOriginal trace level: OFF New trace level: DEBUG

Troubleshooting

The following examples show problems which may be encountered when using enumerated types.

IllegalArgumentException When Attempting to Instantiate an Enum Type

As has been mentioned, instantiation of enum types is forbidden. The EnumTrouble example attempts this.

import java.lang.reflect.Constructor;import java.lang.reflect.InvocationTargetException;import static java.lang.System.out;

enum Charge { POSITIVE, NEGATIVE, NEUTRAL; Charge() {

out.format("under construction%n"); }}

public class EnumTrouble {

93

Page 94: Java Ref Election

public static void main(String... args) {try { Class<?> c = Charge.class;

Constructor[] ctors = c.getDeclaredConstructors(); for (Constructor ctor : ctors) {

out.format("Constructor: %s%n", ctor.toGenericString()); ctor.setAccessible(true); ctor.newInstance(); }

// production code should handle these exceptions more gracefully

} catch (InstantiationException x) { x.printStackTrace();} catch (IllegalAccessException x) { x.printStackTrace();} catch (InvocationTargetException x) { x.printStackTrace();}

}}$ java EnumTroubleConstructor: private Charge()Exception in thread "main" java.lang.IllegalArgumentException: Cannot reflectively create enum objects at java.lang.reflect.Constructor.newInstance(Constructor.java:511) at EnumTrouble.main(EnumTrouble.java:22)

Tip: It is a compile-time error to attempt to explicitly instantiate an enum because that would prevent the defined enum constants from being unique. This restriction is also enforced in reflective code. Code which attempts to instantiate classes using their default constructors should invoke Class.isEnum() first to determine if the class is an enum.

IllegalArgumentException when Setting a Field with an Incompatible Enum Type

94

Page 95: Java Ref Election

Fields storing enums set with the appropriate enum type. (Actually, fields of any type must be set with compatible types.) The EnumTroubleToo example produces the expected error.

import java.lang.reflect.Field;

enum E0 { A, B }enum E1 { A, B }

class ETest { private E0 fld = E0.A;}

public class EnumTroubleToo { public static void main(String... args) {

try { ETest test = new ETest(); Field f =

test.getClass().getDeclaredField("fld"); f.setAccessible(true);

f.set(test, E1.A); // IllegalArgumentException

// production code should handle these exceptions more gracefully

} catch (NoSuchFieldException x) { x.printStackTrace();} catch (IllegalAccessException x) { x.printStackTrace();}

}}$ java EnumTroubleTooException in thread "main" java.lang.IllegalArgumentException: Can not set E0 field ETest.fld to E1 at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:146) at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:150) at sun.reflect.UnsafeObjectFieldAccessorImpl.set(UnsafeObjectFieldAccessorImpl.java:63) at java.lang.reflect.Field.set(Field.java:657) at EnumTroubleToo.main(EnumTroubleToo.java:16)

95

Page 96: Java Ref Election

Tip:  Strictly speaking, any attempt to set a field of type X to a value of type Y can only succeed if the following statement holds:

X.class.isAssignableFrom(Y.class) == true

The code could be modified to perform the following test to verify whether the types are compatible:

if (f.getType().isAssignableFrom(E0.class)) // compatibleelse // expect IllegalArgumentException

96


Top Related