如何找到Java8方法引用的目标? [英] How can I find the target of a Java8 method reference?

查看:139
本文介绍了如何找到Java8方法引用的目标?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I want to capture calls to a mock object

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

所以在内允许我有一个函数<对象,字符串>

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

Is there any reflecto-magic that will let me find the service from the function created from the method reference?

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

我知道函数总会来从这些方法参考中,所以我很乐意尽可能多地降级。

I know that the Function will always come from these method references, so I'm happy to down-cast as much as is necessary.

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

This is not the same question as the related Is it possible to convert method reference to MethodHandle? because, well for a start it isn't the same question, just in a related area. And even if I can get a MethodHandle, I can't get the target from it.

推荐答案

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

Using the trick from this SO post you can find the target. The important method below is findTarget. As it turns out, lambdas do indeed capture their targets, and you can access them from the SerializedLambda.

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

However, this is a pretty nasty reflection hack and it's likely to break in future versions. I do not condone its usage.

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> {}
}

这篇关于如何找到Java8方法引用的目标?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆