java - 为什么 PropertyDescriptor 和 Reflection 在 1.7 中与在 1.6 中对于泛型的工作效果不同?

标签 java generics java-7

最近,我在维护的一个应用程序中遇到了一种奇怪的行为。源代码使用版本 1.6_33 编译,但在 1.7u21 下运行。客户在没有咨询的情况下决定更改版本,我对他们的选择没有任何影响。

在架构中,一些接口(interface)在一定条件下被参数化,以限制其使用。参数化具体类必须使用定义的具体参数之一。为了方便起见,负责实例化 ConcreteParameter(用于附加进程)的机制使用了 PropertyDescriptor。此行为在版本 6 中运行良好,但在版本 7 中不再有效。

在版本 7 中,当尝试获取参数的类时,返回类型始终为 Parameter 类型,而在版本 6 中,类型为 ConcreteParameter。 java 中出现一种异常,如下例所示。但为什么在这种情况下会起作用?!

我浏览了JLS 7还有java 7 compatibility没有找到这种行为的任何解释。对我来说,没有具体类型是不合逻辑的。有人可以解释一下为什么会发生这种情况吗?反射适用于 getDeclaredMethod(...) 但不适用于 getDeclaredMethods() ,这不是一个错误吗?

提前致谢。

以下示例说明了过去的工作原理:

package fr.free.naoj;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;

import org.junit.Test;

public class PropertyDescriptorAndReflectionTest {

    @Test public void testPropertyDescriptorInnerWithJava7() {
        PropertyDescriptor pd;
        try {
            // Fails
            pd = new PropertyDescriptor("parameter", ConcreteClass.class);
            assertEquals(ConcreteParameter.class, pd.getPropertyType());
        } catch (Exception e) {
            e.printStackTrace();
            fail();
        }
    }

    @Test public void testReflectionOnGenericClassWithJava7() {
        try {
            // Works
            Method m = ConcreteClass.class.getDeclaredMethod("getParameter", new Class<?>[]{});
            m.setAccessible(true);
            assertEquals(ConcreteParameter.class, m.getReturnType());

            // Fails
            for (Method me : ConcreteClass.class.getDeclaredMethods()) {
                me.setAccessible(true);
                if (me.getName().equals("getParameter")) {
                    assertEquals(ConcreteParameter.class, me.getReturnType());
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            fail();
        }
    }

    private class ConcreteClass extends AbstractClass<ConcreteParameter> {
        @Override public ConcreteParameter getParameter() {
            this.parameter = new ConcreteParameter();
            return this.parameter;
        }

        @Override public void setParameter(ConcreteParameter parameter) {
            this.parameter = parameter;
        }
    }

    private abstract class AbstractClass<P extends Parameter> implements Super<P> {
        protected P parameter; 
    }

    private interface Super<P extends Parameter> {
        P getParameter();

        void setParameter(P parameter);
    }

    private class ConcreteParameter implements Parameter {
        @Override public String sayHello() {
            return "hello";
        }
    }

    private interface Parameter {
        String sayHello();
    }
}

最佳答案

让我们从最后开始。

ConcreteClass.getDeclaredMethods() 在 Java 6 和 Java 7 下给出 4 个结果,而不仅仅是 2 个:getParameter() 返回 ConcreteParameter,并且setParameter(ConcreteParameter);而且还有返回参数getParameter()setParameter(Parameter)。 Java 6 和 Java 7 之间唯一的区别是从 getDeclaredMethods 返回方法的顺序。我认为您引用的 Java 7 兼容性文档中提到了这一点。

额外的方法对应于编译器添加的桥接方法,以使泛型能够进行删除,即编译器将在类 ConcreteClass 中生成两个这样的隐藏方法:

public void setParameter(Parameter p) {
    setParameter((ConcreteParameter) p);
}

public Parameter getParameter() {
    return getParameter(); // calls the 'real' one!
}

(这不是合法的 Java 代码,因为不允许有两个仅在返回类型上有所不同的方法,但在字节代码中则没问题,因为返回类型是 JVM 方法签名的一部分。 )

如果您阅读 Class#getDeclaredMethod 的 JavaDoc,我们会看到:

If more than one method with the same parameter types is declared in a class, and one of these methods has a return type that is more specific than any of the others, that method is returned; otherwise one of the methods is chosen arbitrarily.

因此,您所看到的 getDeclaredMethodgetDeclaredMethods 的行为似乎在规范中,为 getDeclaredMethod 返回了最具体的方法,并且getDeclaredMethods 返回多个方法。

但是,您在 PropertyDescriptor 中看到的行为似乎是 bug 6788525 的重现。 。也许它只是采用它找到的第一个方法以及它想要的名称,这意味着它有时会出错。我建议您向 Oracle 提交错误报告。

关于java - 为什么 PropertyDescriptor 和 Reflection 在 1.7 中与在 1.6 中对于泛型的工作效果不同?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16685334/

相关文章:

c++ - 压扁容器容器的通用函数

java - COMM_UP 无法解析为变量

java - RestEasy:警告 NoClassDefFoundError DocumentProvider

java - 为什么 ArrayList<>.add() 不起作用?

java - 如何使用数据对象而不是 String[][]

java - 你能将枚举类传递给方法吗(java)

java - 自定义 Java 初始屏幕 "freezes",直到整个应用程序加载完毕

vb.net - VB中的泛型可以访问共享数据吗?

java - 更新 JDK 版本以匹配 JRE?

java - Runtime.exec 无法在 JDK 7u25 上运行