Get java.lang.reflect. Good luck to current or calling method?

How can one obtain in the code objects of the following types from specific instances of the language function they represent:

  • java.lang.reflect.Executable
  • java.lang.reflect.Parameter

Ideally, the providing method would be a call to the receiving method or the receiving method itself.

Note that I found two types of answers to SO:

  • how to get a parameter from a given executable object (trivial!)
  • how to get the name (not the signature!) of the method from the call stack

None of them help to get a parameter for a specific method, whose name is not unique.

Consider, in particular, how this answer and this one for similar questions mask the choice of the desired method.

Now there is Class.getMethod(String, Class<?>... types) , but it does not seem that it was possible to automatically generate the "types" parameter from an existing method definition?

Use Case:

 public class MyAssertions { private static final String DEFAULT_MESSAGE_NOT_NULL = "Parameter must not be null: "; /* Ideal. */ public static void notNull(Object ref, Parameter param) { if(ref == null) throw new IllegalArgumentException(DEFAULT_MESSAGE_NOT_NULL + param.getName()); } /* Still okay. */ public static void notNull(Object ref, Executable caller, int param) { if(ref == null) throw new IllegalArgumentException(DEFAULT_MESSAGE_NOT_NULL + caller.getParameters()[param].getName()); } /* Hell no! */ public static void notNull(Object ref, Class<?> caller, String method, Object[] paramTypes, int param) { if(ref == null) throw new IllegalArgumentException(DEFAULT_MESSAGE_NOT_NULL + caller.getMethod(method, paramTypes) .getParameters()[param].getName()); } } 
+5
source share
1 answer

You can use bytecode utilities to get information about using bytecode. And then use the line number information in the StackTraceElement to get the method .

Here i am using javassist.

  private static Optional<Method> get(StackTraceElement e) throws NotFoundException, ClassNotFoundException { Class<?> clz = Class.forName(e.getClassName()); int line = e.getLineNumber(); ClassPool pool = ClassPool.getDefault(); CtClass cc = pool.get(clz.getName()); return Observable.fromArray(cc.getDeclaredMethods()) .sorted(Comparator.comparing(m -> m.getMethodInfo().getLineNumber(0))) .filter(m -> m.getMethodInfo().getLineNumber(0) <= line) .map(Optional::of) .blockingLast(Optional.empty()) .map(m -> uncheck(() -> clz.getDeclaredMethod(m.getName(), Arrays.stream(m.getParameterTypes()).map(c -> uncheck(() -> nameToClass(c.getName()))).toArray(Class[]::new)))); } private static Class<?> nameToClass(String name) throws ClassNotFoundException { switch (name) { case "int": return int.class; case "short": return short.class; case "long": return long.class; case "double": return double.class; case "float": return float.class; case "boolean": return boolean.class; case "char": return char.class; case "byte": return byte.class; case "void": return void.class; } if (name.endsWith("[]")) { return Array.newInstance(nameToClass(name.substring(0, name.length() - 2)), 0).getClass(); } return Class.forName(name); } 

uncheck my utility ignores an exception only in lambda, you can just use try-catch. see source here

then we check our code

  public static void main(String[] args) throws Exception { // test normal System.out.println(get(new Exception().getStackTrace()[0])); // test lambda Runnable r = () -> System.out.println(uncheck(() -> get(new Exception().getStackTrace()[0]))); r.run(); // test function testHere(1); } private static void testHere(int i) throws Exception { System.out.println(get(new Exception().getStackTrace()[0])); } 

and conclusion

 Optional[public static void xdean.stackoverflow.java.reflection.Q44563354.main(java.lang.String[]) throws java.lang.Exception] Optional[private static java.util.Optional xdean.stackoverflow.java.reflection.Q44563354.lambda$1() throws java.lang.Exception] Optional[private static void xdean.stackoverflow.java.reflection.Q44563354.testHere(int) throws java.lang.Exception] 

But note that this approach only works for a class that has bytecode in the classpath. If you use a dynamic proxy server, it does not work.

Find a complete code example here

EDIT . You can filter synthetic and bridge methods to get the actual method.

+1
source

Source: https://habr.com/ru/post/1268864/


All Articles