【问题标题】:Testing type matching differences in Java 6 and Java 8测试 Java 6 和 Java 8 中的类型匹配差异
【发布时间】:2018-02-02 11:29:45
【问题描述】:

我将应用程序 JDK 从 1.6 更新到 1.8,但在 IntelliJ 中保持语言级别 1.6。

更新后,我在 assertThat 语句的某些测试中遇到了编译错误,并检查了对象的类型。代码是这样的:

assertThat((Class) myList.get(0).getMyClassType(), is(equalTo(MySubclass.class)));

myList 看起来像这样:

  List<MyClassDefinition> myList = myClassDefinition.getMyClassDefinitions(reader);

MyClassDefinition 和 getMyClassType() 的定义如下:

public class MyClassDefinition {
    private Class<? extends MyClass> classType;

   public Class<? extends MyClass> getMyClassType() {
        return classType;
    }
}

而 MySubclass 是 Myclass 的子类:

  public class MySubclass extends MyClass {
        @Override
        public void initialise() {
            // some action here!
        }
    }

MyClass的定义是

public abstract class MyClass extends AnotherClass<AnotherType>{
  //Definitions
}

Assert is 和 equals 库的导入是这样的:

import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;

hamcrest 版本是 hamcrest-all-1-3

将项目 SDK 更改为 jdk 1.8 后,我收到以下错误消息:

Error:(136, 9) java: no suitable method found for assertThat(java.lang.Class,org.hamcrest.Matcher<java.lang.Class<MySubClass>>)
    method org.hamcrest.MatcherAssert.assertThat(java.lang.String,boolean) is not applicable
      (argument mismatch; java.lang.Class cannot be converted to java.lang.String)
    method org.hamcrest.MatcherAssert.<T>assertThat(java.lang.String,T,org.hamcrest.Matcher<? super T>) is not applicable
      (cannot infer type-variable(s) T
        (actual and formal argument lists differ in length))
    method org.hamcrest.MatcherAssert.<T>assertThat(T,org.hamcrest.Matcher<? super T>) is not applicable
      (cannot infer type-variable(s) T
        (argument mismatch; org.hamcrest.Matcher<java.lang.Class<MySubClass>> cannot be converted to org.hamcrest.Matcher<? super java.lang.Class>))

应用程序是用 Ant 构建的,我们已经将 hamcrest jar 文件添加到类文件中,所以当我们在项目中更改 JDK 时没有任何变化。 所以我的问题是为什么当我使用相同级别的语言(1.6)进行编译时,这段代码可以与 JDK 1.6 一起使用,但不能与 1.8 一起使用?它是否依赖于不同的库?

【问题讨论】:

  • 您在这里遗漏了重要的细节。 MyClass 是什么样的? myList 是什么? taskClass 是什么?静态方法assertThatisequalTo 来自哪些类?请写MCVE
  • @Michael OK 我添加了信息
  • 我希望initialise() 在构造函数中被调用。
  • @JoopEggen 不,不是,但没关系。正如我所说,我收到编译错误。所以 JDK 1.8 不认为我们这里有正确的语法,但 jdk 1.6 没有问题

标签: junit java-8 java-6 hamcrest


【解决方案1】:

显然,您通过向 原始类型 Class 插入类型转换来解决的泛型类型签名存在问题,这导致编译器将整个语句视为 unchecked 操作仅使用 原始类型

正如您从编译器消息org.hamcrest.Matcher&lt;java.lang.Class&lt;MySubClass&gt;&gt; cannot be converted to org.hamcrest.Matcher&lt;? super java.lang.Class&gt; 中看到的那样,它现在进行了(正确地)失败的泛型类型检查,但恕我直言,它不应该根据 Java 6 语言规则进行该类型检查。问题源于 JDK8 不包含原始 Java 6 编译器,而是让新编译器尝试使用旧规则进行编译,这可能不是那么精确。

但无论该行为是否正确,我都不希望对其进行修复,因为模拟旧的 Java 6 语言规则并不是最重要的。

请注意,使用源代码级别 1.8,即使没有 原始类型 强制转换,代码也可以毫无问题地编译。最初的问题源于CoreMatchers.equalTo(…) 的限制性签名。独立表达式equalTo(InstanceOfFoo) 返回一个Matcher&lt;Foo&gt;,将其传递给assertThat 只允许测试Foo 的实例(使用isA(Foo.class) 时更糟,它也只能测试Foo 的实例,这使得测试毫无意义)。

使用 Java 8,有目标类型推断,例如
Matcher&lt;Object&gt; m = equalTo(InstanceOfFoo);,它将推断Object&lt;T&gt; 并通过,因为InstanceOfFoo 也可分配给Object。这也可以与您的问题的复合 assertThat 语句结合使用,从而推断出合适的类型。

这将引导我们找到适用于所有语言级别的通用解决方案。只需使用

assertThat(myList.get(0).getMyClassType(), is(equalTo((Object)MySubclass.class)));

Class&lt;MySubclass&gt; 当然可以分配给Object,这使得演员表成为空操作。但是,获得Matcher&lt;Object&gt;,您可以检查您喜欢的每个对象,包括myList.get(0).getMyClassType() 的结果,当然也可以分配给Object。与您最初的解决方法不同,这不承担任何 原始类型 的使用或 unchecked 操作。

您也可以使用显式类型而不是强制类型转换 CoreMatchers.&lt;Object&gt;equalTo(MySubclass.class),但这消除了 import static 的好处。

顺便说一句,当匹配Class对象时,你可以使用sameInstance而不是equalTo

【讨论】:

  • 感谢您的完整回答。该代码是我得到的旧代码,并试图更新到 java 8。有趣的是,如果源代码级别是 1.8,它的编译没有问题。我会更新测试。
猜你喜欢
  • 2021-08-19
  • 2023-03-04
  • 2014-03-19
  • 1970-01-01
  • 2021-05-25
  • 1970-01-01
  • 1970-01-01
  • 2020-06-29
  • 1970-01-01
相关资源
最近更新 更多