如何找到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> {}
}