Gets method type information

A method declaration consists of the method name, descriptor, parameter, return type, and exception. You can get this information through the java.lang.Reflect. Method class. The following example shows how to get all methods in a class by name, return types, parameters, exceptions, and so on.

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(); }}}Copy the code

Run:

$ 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]Copy the code

Public Constructor

getConstructor(Class
… ParameterTypes). First, notice that the return and parameter types are generic types. If in a class file provides the Signature Attribute Method. The getGenericReturnType () calculate the generic type. If not, use method.getreturnType (), which does not carry generic information. Second, note that parameterTypes is a mutable parameter of type java.lang.class. A mutable argument list is represented as a one-dimensional array. You can use method.isvarargs () to distinguish mutable arguments. If the return value is generic, run as follows:

$ 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.ObjectCopy the code

For overloaded functions, all overloaded functions are displayed:

$ 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;]]]]Copy the code
Gets the parameter name of the method

Can through the Java. Lang. Reflect. The Executable. GetParameters () Method to obtain the parameter name, Method and Constructor are inherited and Java. Lang. Reflect. The Executable, All can use this function. The class file does not, by default, store method information. This is because many tools do not want to have a lot of parameter names when generating and retrieving class files. Especially if these tools work with large class files, the JVM will use more memory. In addition, parameter names, such as secret or password, may reveal security-sensitive information. You can use -parameters to save the parameter name to a class file at compile time. The following example shows how to get parameter information:

import java.lang.reflect.*; import java.util.function.*; import static java.lang.System.out; public class MethodParameterSpy { private static final String fmt = "%24s: %s%n"; // for the morbidly curious <E extends RuntimeException> void genericThrow() throws E {} public static void printClassConstructors(Class c) { Constructor[] allConstructors = c.getConstructors(); out.format(fmt, "Number of constructors", allConstructors.length); for (Constructor currentConstructor : allConstructors) { printConstructor(currentConstructor); } Constructor[] allDeclConst = c.getDeclaredConstructors(); out.format(fmt, "Number of declared constructors", allDeclConst.length); for (Constructor currentDeclConst : allDeclConst) { printConstructor(currentDeclConst); } } public static void printClassMethods(Class c) { Method[] allMethods = c.getDeclaredMethods(); out.format(fmt, "Number of methods", allMethods.length); for (Method m : allMethods) { printMethod(m); } } public static void printConstructor(Constructor c) { out.format("%s%n", c.toGenericString()); Parameter[] params = c.getParameters(); out.format(fmt, "Number of parameters", params.length); for (int i = 0; i < params.length; i++) { printParameter(params[i]); } } public static void printMethod(Method m) { out.format("%s%n", m.toGenericString()); out.format(fmt, "Return type", m.getReturnType()); out.format(fmt, "Generic return type", m.getGenericReturnType()); Parameter[] params = m.getParameters(); for (int i = 0; i < params.length; i++) { printParameter(params[i]); } } public static void printParameter(Parameter p) { out.format(fmt, "Parameter class", p.getType()); out.format(fmt, "Parameter name", p.getName()); out.format(fmt, "Modifiers", p.getModifiers()); out.format(fmt, "Is implicit?" , p.isImplicit()); out.format(fmt, "Is name present?" , p.isNamePresent()); out.format(fmt, "Is synthetic?" , p.isSynthetic()); } public static void main(String... args) { try { printClassConstructors(Class.forName(args[0])); printClassMethods(Class.forName(args[0])); } catch (ClassNotFoundException x) { x.printStackTrace(); }}}Copy the code

Remember to use -parameter when compiling, and the result is as follows:

Number of constructors: 1 Constructor #1 public ExampleMethods() Number of declared constructors: 1 Declared constructor #1 public ExampleMethods() Number of methods: 4 Method #1 public boolean ExampleMethods.simpleMethod(java.lang.String,int) Return type: boolean Generic return type: boolean Parameter class: class java.lang.String Parameter name: stringParam Modifiers: 0 Is implicit? : false Is name present? : true Is synthetic? : false Parameter class: int Parameter name: intParam Modifiers: 0 Is implicit? : false Is name present? : true Is synthetic? : false Method #2 public int ExampleMethods.varArgsMethod(java.lang.String...) Return type: int Generic return type: int Parameter class: class [Ljava.lang.String; Parameter name: manyStrings Modifiers: 0 Is implicit?: false Is name present?: true Is synthetic?: false Method #3 public boolean ExampleMethods.methodWithList(java.util.List<java.lang.String>) Return type: boolean Generic return type: boolean Parameter class: interface java.util.List Parameter name: listParam Modifiers: 0 Is implicit?: false Is name present?: true Is synthetic?: false Method #4 public <T> void ExampleMethods.genericMethod(T[],java.util.Collection<T>) Return type: void Generic return type: void Parameter class: class [Ljava.lang.Object; Parameter name: a Modifiers: 0 Is implicit?: false Is name present?: true Is synthetic?: false Parameter class: interface java.util.Collection Parameter name: c Modifiers: 0 Is implicit?: false Is name present?: true Is synthetic?: falseCopy the code

The Parameter class method is used with:

  • GetType (): Returns a Class object indicating the type of the parameter
  • GetName (): Returns the parameter name. Returns the name if there is a name, and argN is the order of the arguments if there is no synthetic argument name.

For example, instead of using -parameter:

public boolean ExampleMethods.simpleMethod(java.lang.String,int) Return type: boolean Generic return type: boolean Parameter class: class java.lang.String Parameter name: arg0 Modifiers: 0 Is implicit? : false Is name present? : false Is synthetic? : false Parameter class: int Parameter name: arg1 Modifiers: 0 Is implicit? : false Is name present? : false Is synthetic? : falseCopy the code
  • GetModifiers (): Returns numbers that represent modifiers.
  • IsImplicit () : Returns true if the argument is implicitly declared
  • IsNamePresent (): Returns true if the class has parameter name information.
  • IsSynthetic (): Returns true if arguments are neither shown nor implicitly declared in the source.
Implicit and synthetic parameters

Some constructors are implicitly declared in the source code. ExampleMethods that do not have an explicitly declared constructor, for example, default to an implicit constructor. Such as:

Number of declared constructors: 1
public ExampleMethods()Copy the code

For inner functions:

public class MethodParameterExamples {
    public class InnerClass { }
}Copy the code

InnerClass is a non-static class that also has an implicit constructor declaration, but the implicit constructor takes one parameter, as follows, and contains a reference to its enclosing function:

public class MethodParameterExamples { public class InnerClass { final MethodParameterExamples parent; InnerClass(final MethodParameterExamples this$0) { parent = this$0; }}}Copy the code

So running MethodParameterExample yields the following result:

public MethodParameterExamples$InnerClass(MethodParameterExamples) Parameter class: class MethodParameterExamples Parameter name: this$0 Modifiers: 32784 Is implicit? : true Is name present? : true Is synthetic? : falseCopy the code

A constructor is marked as synthetic if it is neither explicitly nor implicitly declared but is generated by the Java compiler. Such as:

public class MethodParameterExamples { enum Colors { RED, WHITE; }}Copy the code

For enum Java, complete information is added when compiling:

final class Colors extends java.lang.Enum<Colors> { public final static Colors RED = new Colors("RED", 0); public final static Colors BLUE = new Colors("WHITE", 1); private final static values = new Colors[]{ RED, BLUE }; private Colors(String name, int ordinal) { super(name, ordinal); } public static Colors[] values(){ return values; } public static Colors valueOf(String name){ return (Colors)java.lang.Enum.valueOf(Colors.class, name); }}Copy the code

This is why enum cannot inherit from classes, only interfaces, because it has already sunk enum classes.

Gets and resolves method modifiers

The modifiers for the method are:

  • Access modifiers: pubic, protected, private
  • Static modifier: static
  • Immutable modifier: final
  • Abstract modifier: abstract
  • Synchronized modifier: synchronized
  • Local method modifier: native
  • Strict floating point modifier: strictFP
  • annotations

The following example shows how to get modifier information:

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();
    }
    }
}Copy the code

The running results are as follows:

$ java MethodModifierSpy java.lang.Object wait public final void java.lang.Object.wait() throws java.lang.InterruptedException Modifiers: public final [ 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 native void java.lang.Object.wait(long) throws  java.lang.InterruptedException Modifiers: public final native [ synthetic=false var_args=false bridge=false ] 3 matching overloads found $ java MethodModifierSpy java.lang.StrictMath toRadians public 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 inc private synchronized void MethodModifierSpy.inc() Modifiers: private synchronized [ synthetic=false var_args=false bridge=false ] 1 matching overload found $ java MethodModifierSpy java.lang.Class getConstructor public 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 compareTo public 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 foundCopy the code
The method call

Can use Java. Lang. Reflect. Method. The invoke () Method call reflection access Method. The first argument is the instance of the object being called. In the case of a static function, the first argument may be null, and the following arguments are the arguments of the function. If an error occurs, will be Java. Lang, reflect the abnormal InvocationTargetException packaging, abnormal function of the original can spur InvocationTargetExcetpion. GetCause ().

Find the specified method and execute:

Example:

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(); 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(); }}}Copy the code

Deet calls getDelcaredMethods() to return all methods. Class.isassignablefrom () is used to check whether the argument type matches the argument type of the called method. Because Locale is final, you can use local.class == pType0.getClass() to test this, but class.isAssignableFrom () is more common. Running results:

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

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

Variable argument call:

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(); }}}Copy the code

Running results:

$ java InvokeMain Deet Deet ja JP JP
invoking Deet.main()
invoking testDeet()
Locale = Japanese (Japan,JP), 
ISO Language Code = jpn
testDeet() returned trueCopy the code
Troubleshoot problems
NoSuchMethodException due to type erasure

Example:

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(); }}}Copy the code

Running results:

$ java MethodTrouble lookup java.lang.Integer
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.Object
Found:
  public void MethodTrouble.lookup(T)Copy the code

Because the method declaration has introspective types, the compiler will replace the generic type with its upper bound, which in this case is Object. Therefore, the lookup(Integer) method cannot be found after compilation.

The call is made by throwing an IllegalAccessException
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; class AnotherClass { private void m() {} } 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(); }}}Copy the code

Call result:

$ java MethodTroubleAgain
java.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)Copy the code

Access modifiers cannot be called because they are private.

Invoke () throws an IllegalArgumentException

Example:

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); // works break; case 1: m.invoke(mtt, null); // works (expect compiler warning) break; case 2: Object arg2 = null; m.invoke(mtt, arg2); // IllegalArgumentException break; case 3: m.invoke(mtt, new Object[0]); // works break; case 4: Object arg4 = new Object[0]; m.invoke(mtt, arg4); // IllegalArgumentException break; default: System.out.format("Test not found%n"); } // production code should handle these exceptions more gracefully } catch (Exception x) { x.printStackTrace(); }}}Copy the code

Running results:

$ java MethodTroubleToo 1
PONG!
$ javac MethodTroubleToo.java
MethodTroubleToo.java:16: warning: non-varargs call of varargs method with
  inexact argument type for last parameter;
        m.invoke(mtt, null);           // works (expect compiler warning)
                      ^
  cast to Object for a varargs call
  cast to Object[] for a non-varargs call and to suppress this warning
1 warning
$ java MethodTroubleToo 2
java.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)
$ java MethodTroubleToo 3
PONG!
$ java MethodTroubleToo 4
java.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)Copy the code
The actual call after a method throws an exception thrown InvocationTargetException anomalies

Example:

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(); }}}Copy the code

Running results:

$ java MethodTroubleReturns
drinkMe() failed: I can't drink a negative amount of liquidCopy the code