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
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.
MethodSpy
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();
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 getConstructor
public 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
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 cast
public 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 ...
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 format
public 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.
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.