3

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

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)反射实现只占用了该性能的一小部分。

所以我想用 a 替换 is MethodHandle

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在这种情况下工作?

4

2 回答 2

4

ClassCastException仅当您使用源/目标级别 java 6 编译时才会发生这种情况。

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

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

于 2015-12-03T11:23:03.830 回答
3

在框架/库代码中使用 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,尽管它不允许您自动进行类型转换,例如拆箱。

于 2015-12-02T16:32:59.490 回答