繁体   English   中英

如何找到Java8方法引用的目标?

[英]How can I find the target of a Java8 method reference?

我想捕获对模拟对象的调用

public interface Service {
    public String stringify(Object o);
}
service = mockery.mock(Service.class);
mockery.allowing(service::stringify).with(42).will(() -> "42");

所以在内部allowing我有一个Function<Object, String>

是否有任何reflecto-magic可以让我从方法参考创建的函数中找到服务?

public WithClause allowing(Function<T,R> f) {
    Object myServiceBackAgain = findTargetOf(function);
    ....
}

我知道函数将始终来自这些方法引用,所以我很乐意尽可能地向下转换。

这与相关的问题不同是否可以将方法引用转换为MethodHandle? 因为,一开始它不是同一个问题,只是在相关领域。 即使我可以得到一个MethodHandle,我也无法从中获取目标。

使用此SO帖子中的技巧,您可以找到目标。 下面的重要方法是findTarget 事实证明,lambdas确实捕获了它们的目标,你可以从SerializedLambda访问它们。

然而,这是一个非常讨厌的反射黑客,它可能会在未来的版本中打破。 我不宽恕它的用法。

import java.io.Serializable;
import java.lang.invoke.SerializedLambda;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Optional;
import java.util.function.Function;

public class FindMethodReferenceTarget {
  public static void main(String[] args) {
    String s = "123";
    Optional<Object> target = findTarget(s::charAt);
    System.out.println(target.get().equals(s));

    Object o = new FindMethodReferenceTarget();
    target = findTarget(o::equals);
    System.out.println(target.get().equals(o));
  }

  private static <T, R> Optional<Object> findTarget(
      DebuggableFunction<T, R> methodReference) {
    return getLambda(methodReference).map(l -> l.getCapturedArg(0));
  }

  private static Optional<SerializedLambda> getLambda(Serializable lambda) {
    for (Class<?> cl = lambda.getClass(); cl != null; cl = cl.getSuperclass()) {
      try {
        Method m = cl.getDeclaredMethod("writeReplace");
        m.setAccessible(true);
        Object replacement = m.invoke(lambda);
        if (!(replacement instanceof SerializedLambda)) {
          break; // custom interface implementation
        }
        SerializedLambda l = (SerializedLambda) replacement;
        return Optional.of(l);
      } catch (NoSuchMethodException e) {
        // do nothing
      } catch (IllegalAccessException | InvocationTargetException e) {
        break;
      }
    }

    return Optional.empty();
  }

  @FunctionalInterface
  private static interface DebuggableFunction<T, R> extends
      Serializable,
      Function<T, R> {}
}

找不到目标是没有直接的方法,因为方法引用只是被转换为lambdas(根据定义,匿名)。 所以你需要使用一种解决方法。

大概你熟悉Java 7的代理,因为你已经设法实现了你的mock工厂方法。

解决方法是当有人调用你的allowing方法时,你设置某种全局标志来警告你想要记录下一个调用的所有模拟,然后你调用你给的lambda。 通过查看哪个模拟记录了调用,您现在已经找到了方法引用的目标,并且您可以取消设置全局标志并继续使用其余的模拟框架。

我知道,这很难看。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM