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

标签 java reflection java-8 java-11 methodhandle

这将是一个有点复杂的解释,但我会尝试。

假设你有一个泛型类:

static class Box<T extends Number> {

    private T value;

    public T getValue() {
        return value;
    }

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

}

还有一个允许调用 getValue 的方法以反射(reflection)的方式:
 // 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);
    }
}

这是做什么的
  • 创建 MethodHandlegetValue方法
  • 适应 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
    

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

    当他们 build Box<Long> box = new Box<>();Long类型,提供 Long.class 是很自然的进一步,而不是 Number.class .解决方案显然是微不足道的,但是,我在想如果我可以(在运行时)“看到” getValue 的返回类型是一个泛型类型,我可以抛出一个正确的错误信息。例如:
    "you provided Long.class, but the generic type was erased to ..."
    

    换句话说,如果我可以在运行时告诉返回类型是 Number.class来自 getValue并且这是某些删除的结果,我在以后的决定中可能会更聪明一些。

    那可能吗?

    最佳答案

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

        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)的方式。您可能还需要检查以确保匹配的方法不是抽象的或静态的。 (有人会认为它不能是抽象的,因为您提供了一个实例,但可能存在我没有想到的边缘情况,例如单独编译。)您可能还需要检查该方法是否已声明您正在反射(reflection)的同一个类。由于您关心性能,因此进行反射可能会太慢。但是,如果您只关心诊断,您可以尝试快乐的路径,如果您获得 NSME,请进行反射查找以获得正确的返回类型。

    关于java - 有没有办法判断运行时类型是否被删除,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62351292/

    相关文章:

    java - 使用反向比较器对带有流的数组进行排序

    java - 使用vtd-xml解析xml文件

    c# - 如何创建动态类型 List<T>

    ios - Objective-C 反射;将参数传递给方法

    c# - 使用反射的 .NET 远程处理

    java - 如何正确表示 Java 版本?

    java - MYSQL:添加新行值时的通知

    java - 为 CloudFoundry Java CLI 实现 TokenProvider 时出现问题

    java 8根据属性比较2个列表

    java - 如何在上述情况下使用 Java 8 Optional?