为什么Java类型推断选择了错误的重载?

时间:2015-09-08 19:00:08

标签: java junit

public class MyProperties {
  private Map<String, Object> properties = new HashMap<String, Object>();

  public void setProperty(String name, Object value) {
      properties.put(name, value);
  }

  @SuppressWarnings("unchecked")
  public <T> T getProperty(String name) {
      return (T) properties.get(name);
  }
}

@Test
public void test() {
    MyProperties props1 = new MyProperties();
    props1.setProperty("name", "John Smith");

    MyProperties props2 = new MyProperties();
    props2.setProperty("name", "John Smith");

    assertEquals(props2.getProperty("name"), props1.getProperty("name"));
}

上面的单元测试在我的机器上传递但在我们的jenkins环境中失败并出现以下错误:

java.lang.String cannot be cast to [Ljava.lang.Object;

单元测试通过Eclipse和ant(eclipse 4.4 luna,ant 1.9.6,jdk_8_u60,Windows 7 64bit)在我的机器上传递但在我们的jenkins环境中失败(ant 1.9.6 jdk_8_u60,Ubuntu 12.04.4) 。它在其他几个环境中也失败了,并在其他几个环境中工作 - 没有明显的押韵或理由。

所以我有两个问题:

  

1)为什么Java选择重载org.junit.Assert.assertEquals(Object[],Object[])而不是org.junit.Assert.assertEquals(String,String)org.junit.Assert.assertEquals(Object,Object)

     

2)为什么单元测试在某些环境中传递,而在其他环境中使用相同版本的java,ant和junit失败?

1 个答案:

答案 0 :(得分:3)

关于2:

不同的行为很可能不是由运行时环境引起的,而是由用于编译类的编译器引起的。

Eclipse有自己的Java编译器,而Jenkins使用JDK的javac。

似乎是Eclipse 4.4。生成assertEquals行的调用,允许成功的测试运行,而javac生成对org.junit.Assert.assertEquals(Object[],Object[])的调用,然后测试失败。

这在Eclipse 4.5中不起作用,它将在编译时抱怨对已弃用的方法Assert.assertEquals(Object[],Object[])的调用,并且在Eclipse 4.5中运行时测试也会失败。

您可以通过反编译Eclipse 4.4和javac(使用(javap -c)生成的类来测试假设,并检查他们选择的Assert方法。

关于1:

Assert有两个assertEquals方法接受两个对象:

Assert.assertEquals(Object expected, Object actual);
Assert.assertEquals(Object[] expecteds, Object[] actuals);

根据JLS(15.12),在编译方法调用时,编译器必须从所有适用的方法中选择最具体的方法。在示例中,这是Assert.assertEquals(Object[] expecteds, Object[] actuals)

这可能会让人感到困惑,因为JUnit可能决定使用数组参数弃用该方法。