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

后端 未结 2 1562
栀梦
栀梦 2020-12-10 17:49

I want to capture calls to a mock object

public interface Service {
    public String stringify(Object o);
}
service = mockery.mock(Service.class);
mockery.a         


        
2条回答
  •  萌比男神i
    2020-12-10 18:04

    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 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  Optional findTarget(
          DebuggableFunction methodReference) {
        return getLambda(methodReference).map(l -> l.getCapturedArg(0));
      }
    
      private static Optional 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 extends
          Serializable,
          Function {}
    }
    
        

    提交回复
    热议问题