The core
reflection facility, java.lang.reflect, offers
programmatic access to information about loaded classes. Given a Class object, you can obtain Constructor, Method, and Field instances representing the constructors,
methods, and fields of the class represented by the Class instance. These objects provide programmatic
access to the class’s member names, field types, method signatures, and so on.
Moreover, Constructor, Method, and Field instances let you manipulate their underlying
counterparts reflectively: you can construct instances, invoke
methods, and access fields of the underlying class by invoking methods on the Constructor, Method, and Field instances. For example, ethod.invoke
lets
you invoke any method on any object of any class (subject to the usual security
constraints).
Reflection allows one class to use another, even if the latter class did not
exist when the former was compiled. This power, however, comes at a price:
• You lose all the
benefits of compile-time type checking, including exception checking. If
a program attempts to invoke a nonexistent or inaccessible method reflectively,
it will fail at runtime unless you’ve taken special precautions.
• The code
required to perform reflective access is clumsy and verbose. It is
tedious to write and difficult to read.
• Performance
suffers. Reflective method invocation is much slower than normal method
invocation. Exactly how much slower is hard to say, because there are so many
factors at work. On my machine, the speed difference can be as small as a factor
of two or as large as a factor of fifty.
The core
reflection facility was originally designed for component-based application
builder tools. Such tools generally load classes on demand and use reflection
to find out what methods and constructors they support. The tools let their
users interactively construct applications that access these classes, but the
generated applications access the classes normally, not reflectively.
Reflection is used only at design time. As a rule,
objects should not be accessed reflectively in normal applications at runtime.
You can obtain
many of the benefits of reflection while incurring few of its costs by using it
only in a very limited form. For many programs that must use a class that
is unavailable at compile time, there exists at compile time an appropriate
interface or superclass by which to refer to the class (Item 52). If this is
the case, you can create instances reflectively and access them normally via their
interface or superclass. If the appropriate constructor has no
parameters, then you don’t even need to use java.lang.reflect; the Class.newInstance method provides the required functionality.
For example,
here’s a program that creates a Set<String>
instance
whose class is specified by the first command line argument.
The order in
which these arguments are printed, however, depends on the class specified in
the first argument. If you specify java.util.HashSet, they’re
printed in apparently random order; if you specify java.util.TreeSet, they’re printed in alphabetical order, as
the elements in a TreeSet are sorted:
// Reflective instantiation with interface access
public static void main(String[] args) {
// Translate the class name into a Class object
Class<?> cl = null;
try {
cl = Class.forName(args[0]);
} catch(ClassNotFoundException e) {
System.err.println("Class not found.");
System.exit(1);
}
// Instantiate the class
Set<String> s = null;
try {
s = (Set<String>) cl.newInstance();
} catch(IllegalAccessException e) {
System.err.println("Class not accessible.");
System.exit(1);
} catch(InstantiationException e) {
System.err.println("Class not instantiable.");
System.exit(1);
}
// Exercise the set
s.addAll(Arrays.asList(args).subList(1, args.length));
System.out.println(s);
}
This example
demonstrates two disadvantages of reflection. First, the example can generate
three runtime errors, all of which would have been compile-time errors if
reflective instantiation were not used. Second, it takes twenty lines of
tedious code to generate an instance of the class from its name, whereas a
constructor invocation would fit neatly on a single line. These disadvantages
are, however, restricted to the part of the program that instantiates the
object. Once instantiated, it is indistinguishable from any other Set instance. In a real program, the great bulk
of the code is thus unaffected by this limited use of reflection.
If you try
compiling the program, you’ll get the following error message:
Note: SetEx.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
This warning
concerns the program’s use of generic types, but it does not indicate a real
problem. To learn the best way to suppress the warning, see Item 24. Another
tangential issue that deserves note is this program’s use of System. exit. It is rarely appropriate to call this
method, which terminates the entire VM. It is, however, appropriate for
abnormal termination of a command line utility.
In summary,
reflection is a powerful facility that is required for certain sophisticated
system programming tasks, but it has many disadvantages. If you are writing a
program that has to work with classes unknown at compile time, you should, if
at all possible, use reflection only to instantiate objects, and access the
objects using some interface or superclass that is known at compile time.
Reference: Effective Java 2nd Edition by Joshua Bloch
Reference: Effective Java 2nd Edition by Joshua Bloch