2013-05-22 34 views
2

最近我遇到了一个奇怪的行为,我维护一个应用程序。源代码版本为1.6_33,但运行于1.7u21以下。客户决定在没有咨询的情况下更改版本,我对他们的选择没有影响。为什么PropertyDescriptor和Reflection在1.6中与泛型不一样?

在架构中,某些接口在特定条件下被参数化以限制其使用。参数化的具体类必须使用定义的具体参数之一。为了方便起见,PropertyDescritor被负责实例化ConcreteParameter(用于其他进程)的机制使用。这种行为与第6版效果不错,但没有更多的与版本7

第7版,试图获取类的参数时,返回类型总是型参数的,而在6个版型是ConcreteParameter。在java中发生一个异常,并在下面的示例中介绍。但为什么在这种情况下工作?!

我浏览了JLS 7java 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(); 
    } 
} 

回答

2

让我们从月底开始。

ConcreteClass.getDeclaredMethods()在Java 6和Java 7下给出4个结果,而不仅仅是2:getParameter()返回ConcreteParametersetParameter(ConcreteParameter);还有getParameter()返回ParametersetParameter(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方法签名的一部分。)

如果您阅读JavaDoc for Class#getDeclaredMethod我们看到这个:

如果在类中声明了多个具有相同参数类型的方法,并且其中一个方法的返回类型比其他方法更具体,则返回该方法;否则其中一种方法是任意选择的。

所以,你看到了getDeclaredMethodgetDeclaredMethods的行为似乎是在规格,返回最具体的方法getDeclaredMethodgetDeclaredMethods返回了多个方法。

但是,您看到的行为与PropertyDescriptor看到的是bug 6788525的重复。也许它只是采取第一种方法,它找到了它想要的名称,这意味着它有时会出错。我建议你向Oracle提交一份错误报告。

+0

感谢rxg为您的审查和答案。事实上,getDeclaredMethod和getDeclaredMethods的行为似乎是合乎逻辑的。 而且我也会向Oracle汇报。 – Naoj

相关问题