获取当前或调用方法的java.lang.reflect.Executable?

时间:2017-06-15 09:06:28

标签: java reflection java-8

如何从代码中的代表语言功能的特定实例中获取以下类型的对象:

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

理想情况下,提供方法将是获取方法或获取方法本身的调用者。

请注意,我在SO上找到了两种答案:

  • 如何从给定的可执行对象获取参数(平凡!)
  • 如何从调用堆栈中获取方法的名称(不是签名!)

无论是将参数提供给名称不唯一的特定方法都有帮助。

特别考虑类似问题this answerthis one如何掩盖所选方法的选择。

现在,存在Class.getMethod(String, Class<?>... types),但似乎没有人可以从现有方法定义自动生成“types”参数?

用例:

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

1 个答案:

答案 0 :(得分:1)

您可以使用字节码实用程序来获取字节码信息。然后使用StackTraceElement中的行号信息获取method

这里我使用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是我的实用程序只忽略lambda中的异常,你可以简单地使用try-catch。 see source here

然后我们测试我们的代码

  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]));
  }

和输出

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]

但请注意,此方法仅适用于类在classpath中具有字节码。如果您使用动态代理,它就无法运行。

查找完整的示例代码here

编辑:您可以过滤合成和桥接方法以获取实际方法。