最近,我在维护的一个应用程序中遇到了一种奇怪的行为。源代码使用版本 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.
因此,您所看到的 getDeclaredMethod
和 getDeclaredMethods
的行为似乎在规范中,为 getDeclaredMethod
返回了最具体的方法,并且getDeclaredMethods
返回多个方法。
但是,您在 PropertyDescriptor
中看到的行为似乎是 bug 6788525 的重现。 。也许它只是采用它找到的第一个方法以及它想要的名称,这意味着它有时会出错。我建议您向 Oracle 提交错误报告。
关于java - 为什么 PropertyDescriptor 和 Reflection 在 1.7 中与在 1.6 中对于泛型的工作效果不同?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16685334/