这将是一个有点复杂的解释,但我会尝试。
假设你有一个泛型类:
static class Box<T extends Number> {
private T value;
public T getValue() {
return value;
}
public void setValue(T value) {
this.value = value;
}
}
getValue
还有一个允许以反射方式调用的方法:
// it's just an example, the real world scenario is slightly more involved
private static final Lookup LOOKUP = MethodHandles.lookup();
public static <T, R> T result(String methodName, Class<T> propertyClass, R instance) {
try {
/* line1 */
MethodHandle handle = LOOKUP.findVirtual(
instance.getClass(),
methodName,
MethodType.methodType(propertyClass)
);
/* line2 */
handle = handle.asType(handle.type()
.changeReturnType(Object.class)
.changeParameterType(0, Object.class));
/* line3 */
Object obj = handle.invokeExact(instance);
return propertyClass.cast(obj);
} catch (Throwable t) {
throw new RuntimeException(t);
}
}
这是做什么的
创建一个
MethodHandle
方法getValue
调整它
MethodHandle
,以便我可以调用invokeExact
它(否则我需要调用invoke
它,这会更慢)。但这一步完全是可选的。一旦我建立
MethodHandle
,调用它。
现在让我们尝试调用它:
public static void main(String[] args) throws Throwable {
Box<Long> box = new Box<>();
box.setValue(42L);
result("getValue", Long.class, box);
}
这应该有效,对吧?嗯,不。这将失败:
Caused by: java.lang.NoSuchMethodException: no such method: GenericTest$Box.getValue()Long/invokeVirtual
我明白为什么,因为is的擦除类型,所以调用应该是:T extends Number
Number
result("getValue", Number.class, box); // not Long.class
这对我来说是显而易见的,但对我工作场所图书馆的来电者来说却不是,我不能责怪他们。请注意,这是一个简化的示例...
Box<Long> box = new Box<>();
当他们使用Long
类型构建时,提供Long.class
进一步而不是Number.class
. 解决方案显然是微不足道的,但是,我在想如果我可以(在运行时)“看到”返回类型getValue
是泛型类型,我可以抛出一个正确的错误消息。例如:
"you provided Long.class, but the generic type was erased to ..."
换句话说,如果我可以在运行时判断返回类型是Number.class
fromgetValue
并且它是某个erasure的结果,那么我在以后的决定中可能会更聪明一些。
那可能吗?