MethodHandle 可以被 frameworks/libraries 使用(而不是传统的反射)吗?
Can MethodHandle be used by frameworks/libraries (instead of traditional reflection)?
在我的框架中,我有一个 class 这样的:
public class Foo<B, V> {
private final Method getterMethod;
...
public V executeGetter(B bean) {
try {
return getterMethod.invoke(bean);
} catch ...
}
}
此 class 用于调用用户创建的 classes 的 getter,这些 getter 在我的框架的编译时不可用。例如,B
可能是一个名为 Person
的 class。
通过分析,我发现这种方法非常慢。 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)
即使 bean
是 Person
的实例。现在误导的部分是它试图将 Object[]
(而不是 Object
)转换为 Person
。请注意,将其包装在对象数组中(这是一种性能损失)没有帮助:
return getterMethodHandle.invoke(new Object[]{bean}); // Same exception
在这种情况下是否可以让 MethodHandle
工作?
在 framework/library 代码中使用 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
,尽管它不允许您自动进行类型转换,例如拆箱。
仅当您使用 source/target 级别 java 6.
编译时才会出现 ClassCastException
使用 source/target 7 级或更高级别 编译以避免 ClassCastException
.
由于 Tagir 的回答找到了答案。 (也为他的答案投票)
在我的框架中,我有一个 class 这样的:
public class Foo<B, V> {
private final Method getterMethod;
...
public V executeGetter(B bean) {
try {
return getterMethod.invoke(bean);
} catch ...
}
}
此 class 用于调用用户创建的 classes 的 getter,这些 getter 在我的框架的编译时不可用。例如,B
可能是一个名为 Person
的 class。
通过分析,我发现这种方法非常慢。 Method.invoke()
在采样分析中占用 40% 的性能(即使使用 setAccessible(true)
),而非反射实现只占用该性能的一小部分。
所以我想用 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)
即使 bean
是 Person
的实例。现在误导的部分是它试图将 Object[]
(而不是 Object
)转换为 Person
。请注意,将其包装在对象数组中(这是一种性能损失)没有帮助:
return getterMethodHandle.invoke(new Object[]{bean}); // Same exception
在这种情况下是否可以让 MethodHandle
工作?
在 framework/library 代码中使用 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
,尽管它不允许您自动进行类型转换,例如拆箱。
仅当您使用 source/target 级别 java 6.
编译时才会出现ClassCastException
使用 source/target 7 级或更高级别 编译以避免 ClassCastException
.
由于 Tagir 的回答找到了答案。 (也为他的答案投票)