简体   繁体   English

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

[英]Is there a way to tell if the runtime type was erased

This is going to be a bit complicated to explain, but I will try.这将是一个有点复杂的解释,但我会尝试。

Suppose you have a generic class:假设您有一个通用 class:

static class Box<T extends Number> {

    private T value;

    public T getValue() {
        return value;
    }

    public void setValue(T value) {
        this.value = value;
    }

}

And a method that allows to call the getValue in a reflective way:还有一个允许以反射方式调用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);
    }
}

What this does is这是做什么的

  • create a MethodHandle to the getValue methodgetValue方法创建一个MethodHandle

  • adapt that MethodHandle , so that I could call invokeExact on it (otherwise I would need to call invoke , which is slower).调整MethodHandle ,以便我可以在其上调用invokeExact (否则我需要调用invoke ,这会更慢)。 But this step is entirely optional.但这一步完全是可选的。

  • once I build the MethodHandle , invoke it.一旦我构建了MethodHandle ,就调用它。

And now let's try to call this:现在让我们尝试调用它:

public static void main(String[] args) throws Throwable {
    Box<Long> box = new Box<>();
    box.setValue(42L);
    result("getValue", Long.class, box);
}

This should work, right?这应该有效,对吧? Well, no.嗯,没有。 This will fail with:这将失败:

 Caused by: java.lang.NoSuchMethodException: no such method: GenericTest$Box.getValue()Long/invokeVirtual

I understand why, because the erased type of T extends Number is Number , so the invocation is really supposed to be:我明白为什么,因为T extends Number擦除类型是Number ,所以调用应该是:

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

This is obvious to me, but not to the callers of the library at my work-place and I can't blame them.这对我来说是显而易见的,但对我工作场所图书馆的来电者来说却不是,我不能责怪他们。 Please note that this is a simplified example...请注意,这是一个简化的示例...


When they build Box<Long> box = new Box<>();当他们构建Box<Long> box = new Box<>(); with a Long type, it is sort of natural to provide Long.class further, instead of Number.class .对于Long类型,提供Long.class是很自然的,而不是Number.class The solution is obviously trivial, but, I was thinking that if I could (at runtime) "see" that the return type of getValue was a generic type, I could throw a proper error message.解决方案显然是微不足道的,但是,我在想,如果我可以(在运行时)“看到” getValue的返回类型是泛型类型,我可以抛出一个正确的错误消息。 For example:例如:

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

In other words if I could tell at runtime that the return type is Number.class from getValue and that it is the result of some erasure , I could be a bit smarter in the later decisions.换句话说,如果我可以在运行时告诉我返回类型是来自getValueNumber.class并且它是一些擦除的结果,那么我在以后的决定中可能会更聪明一些。

Is that possible?那可能吗?

Well, maybe you could use good old reflection.好吧,也许你可以使用好的旧反射。 Using reflection allows you to look up a method by name and parameter types but not return type.使用反射允许您通过名称和参数类型而不是返回类型来查找方法。 You can then inspect the return type to see if the caller provided the right one:然后,您可以检查返回类型以查看调用者是否提供了正确的类型:

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

You might need to adjust how you do the reflective lookup (getMethod or getDeclaredMethod) based on your needs.您可能需要根据需要调整反射查找(getMethod 或 getDeclaredMethod)的方式。 You might also need to check to ensure the matched method isn't abstract or static.您可能还需要检查以确保匹配的方法不是抽象的或 static。 (One would think it cannot be abstract because you're provided with an instance, but there might be an edge case I haven't thought of, such as separate compilation.) And you might also need to check that the method is declared on the same class you're reflecting on. (有人会认为它不能是抽象的,因为您提供了一个实例,但可能存在我没有想到的边缘情况,例如单独编译。)您可能还需要检查该方法是否声明在您正在反映的相同 class 。 Since you're concerned about performance, doing reflection might be too slow.由于您关心性能,因此进行反射可能会太慢。 But if all you're concerned about is diagnostics, you could try the happy path, and if you get a NSME, do a reflective lookup to get the right return type.但是,如果您只关心诊断,您可以尝试快乐的路径,如果您获得 NSME,请进行反射查找以获得正确的返回类型。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM