【问题标题】:Obtain java.lang.reflect.Executable of current or calling method?获取当前或调用方法的 java.lang.reflect.Executable?
【发布时间】:2017-12-13 10:36:26
【问题描述】:

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

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

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

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

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

都不有助于获取名称不唯一的特定方法的参数。

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

现在,存在Class.getMethod(String, Class<?>... 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());
  }
}

【问题讨论】:

  • 只是我还是您需要提供一个示例来说明您想要实现的目标?
  • 恐怕,这是不可能的。另一方面,如果你在方法内部,你已经知道方法、它的签名等等。使用这些信息会直接阻碍复制和粘贴编程或重构,但无论如何这些都不应该是您的主要开发风格。 ——哦,好吧,如果你可以为调用者做这件事,也许可以开发一些简洁的工具,但是,标准 API 不支持它,甚至 Java 9 的堆栈遍历器也不支持……
  • @Eugene :给你。
  • 用例不包括实际用例,即这些方法的潜在调用者。有一个技巧,如果性能无关紧要,调用者如何获得他们自己的Method 对象,但如果你想要的只是参数名称,你甚至会同时引入对参数位置的依赖关系,没有多大意义。 Objects.requireNonNull(myParam, "myParam") 不是比MyAssertions.notNull(myParam, 42) 更简洁更简洁吗?特别是,因为后者仅在方法已使用特殊选项编译时才提供名称以保留方法参数名称?

标签: java reflection java-8


【解决方案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]

但请注意,这种方法仅适用于类路径中有字节码的类。如果使用动态代理,则不起作用。

找到完整的示例代码here

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-01-07
    • 2011-01-04
    • 2011-01-09
    • 2016-03-13
    • 1970-01-01
    相关资源
    最近更新 更多