有没有办法判断运行时类型是否被删除



解释起来会有点复杂,但我会尝试。

假设您有一个泛型类:

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);
}
}

这样做是

  • 创建对getValue方法的MethodHandle

  • 调整该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

我明白为什么,因为T extends Number的擦除类型是Number,所以调用实际上应该是:

result("getValue", Number.class, box); // not Long.class

这对我来说是显而易见的,但对我工作场所图书馆的来电者来说却不是,我不能责怪他们。请注意,这是一个简化的示例...


当他们使用Long类型构建Box<Long> box = new Box<>();时,很自然地提供进一步Long.class,而不是Number.class。解决方案显然是微不足道的,但是,我在想,如果我能(在运行时("看到"getValue的返回类型是泛型类型,我可以抛出正确的错误消息。例如:

"you provided Long.class, but the generic type was erased to ..."

换句话说,如果我能在运行时告诉返回类型是从getValueNumber.class的,并且它是一些擦除的结果,那么我可以在以后的决策中更聪明一些。

这可能吗?

好吧,也许你可以使用好的旧反射。使用反射可以按名称和参数类型查找方法,但不能按返回类型查找方法。然后,您可以检查返回类型以查看调用方是否提供了正确的返回类型:

Method method = instance.getClass().getMethod(methodName);
Class<?> rtype = method.getReturnType();
if (rtype != propertyClass) {
throw new IllegalArgumentException("must use " + rtype + " instead of " + propertyClass);
}
MethodHandle handle = LOOKUP.unreflect(method);

您可能需要根据需要调整反射查找(getMethod 或 getDeclaredMethod(的方式。您可能还需要检查以确保匹配的方法不是抽象的或静态的。(有人会认为它不可能是抽象的,因为你提供了一个实例,但可能有一个我没有想到的边缘情况,比如单独的编译。您可能还需要检查该方法是否在您正在反映的同一类上声明。由于您担心性能,因此进行反思可能太慢。但是,如果您只关心诊断,则可以尝试快乐路径,如果您获得NSME,请执行反射查找以获得正确的返回类型。

最新更新