【问题标题】:Can MethodHandle be used by frameworks/libraries (instead of traditional reflection)?框架/库可以使用 MethodHandle(而不是传统的反射)吗?
【发布时间】:2015-12-03 19:23:03
【问题描述】:

在我的框架中,我有一个这样的类:

public class Foo<B, V> {
    private final Method getterMethod;
    ...

    public V executeGetter(B bean) {
        try {
           return getterMethod.invoke(bean);
        } catch ...
    }
}

此类用于调用由用户创建的类的 getter,这些类在我的框架编译时不可用。例如,B 可能是一个名为 Person 的类。

通过分析,我发现这种方法非常慢。 Method.invoke() 在采样分析中占用了 40% 的性能(即使使用 setAccessible(true)),而非反射实现仅占用该性能的一小部分。

所以我想用 MethodHandle 替换 is:

public class Foo<B, V> {
    private final MethodHandle getterMethodHandle;
    ...

    public V executeGetter(B bean) {
        try {
           return getterMethodHandle.invoke(bean);
        } catch ...
    }
}

然后我得到了这个异常:

java.lang.ClassCastException: Cannot cast [Ljava.lang.Object; to Person
    at sun.invoke.util.ValueConversions.newClassCastException(ValueConversions.java:461)
    at sun.invoke.util.ValueConversions.castReference(ValueConversions.java:456)
    at ...Foo.executeGetter(Foo.java:123)

即使beanPerson 的一个实例。现在误导部分是它试图将Object[](而不是Object)转换为Person。请注意,将其包装在对象数组中(这是一种性能损失)并没有帮助:

 return getterMethodHandle.invoke(new Object[]{bean}); // Same exception

是否可以让MethodHandle 在这种情况下工作?

【问题讨论】:

  • 你能添加完整的堆栈跟踪吗?
  • 你是如何构建getterMethodHandle的?
  • 添加了堆栈跟踪。使用 JDK 7 进行记录。
  • 句柄构造如下:getterMethodHandle = lookup.unreflect(getterMethod);

标签: java reflection methodhandle


【解决方案1】:

ClassCastException 仅在您使用源/目标级别 java 6 编译时才会出现。

使用 7 级或更高级别的源/目标编译以避免ClassCastException

感谢 Tagir 的回答,找到了答案。 (也给他的答案投票)

【讨论】:

    【解决方案2】:

    在框架/库代码中使用 MethodHandles 非常好,我认为您的代码没有问题。这个例子工作得很好:

    import java.lang.invoke.MethodHandle;
    import java.lang.invoke.MethodHandles;
    import java.lang.invoke.MethodType;
    
    public class Foo<B, V> {
        private final MethodHandle getterMethodHandle;
    
        public Foo(MethodHandle mh) {
            this.getterMethodHandle = mh;
        }
    
        public V executeGetter(B bean) {
            try {
               return (V) getterMethodHandle.invoke(bean);
            } catch(RuntimeException | Error ex) {
                throw ex;
            } catch(Throwable t) {
                throw new RuntimeException(t);
            }
        }
    
        static class Pojo {
            String x;
    
            public Pojo(String x) {
                this.x = x;
            }
    
            public String getX() {
                return x;
            }
        }
    
        public static void main(String[] args) throws Exception {
            // I prefer explicit use of findXYZ
            Foo<Pojo, String> foo = new Foo<>(MethodHandles.lookup()
                    .findVirtual(Pojo.class, "getX", MethodType.methodType(String.class)));
            // Though unreflect also works fine
            Foo<Pojo, String> foo2 = new Foo<>(MethodHandles.lookup()
                    .unreflect(Pojo.class.getMethod("getX")));
    
            System.out.println(foo.executeGetter(new Pojo("foo")));
            System.out.println(foo2.executeGetter(new Pojo("bar")));
        }
    }
    

    输出是:

    foo
    bar
    

    要获得更好的性能,请考虑使用invokeExact,尽管它不允许您自动进行类型转换,例如拆箱。

    【讨论】:

    • 非常有趣。您确实没有在 executeGetter 方法中编译时间引用 Pojo。我需要调查为什么它在我的情况下不起作用。
    • 在具有源和目标级别 Java 6 的 JDK 1.7 中,此确切代码引发相同的异常java.lang.RuntimeException: java.lang.ClassCastException: Cannot cast [Ljava.lang.Object; to org.optaplanner.core.impl.domain.common.Foo$Pojo at ...Foo.executeGetter(Foo.java:34)
    • 并且使用相同的 JDK (1.7) 但源和目标级别的 Java 7 确实可以工作(与源/目标 java 6 不同)。
    • @GeoffreyDeSmet,这实际上是意料之中的。 invoke/invokeExact 方法上有特殊的注解 @PolymorphicSignature,自 Java 7 起由 java 编译器专门处理,但当目标是 Java 6 或更低版本时,这样的注解毫无意义,会导致错误的字节码生成。
    • 现在说得通了,尽管如果 JDK 7 正在编译任何使用 MethodHandles 且目标级别为 java 6 的代码,它应该很快就会失败。谢谢! PS:I am baffled again for another reason with methodhandles
    猜你喜欢
    • 2020-08-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-04-01
    • 2020-12-08
    • 2021-01-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多