Java类型推断疑问:为何这段Hamcrest代码可编译通过?
问题解析:Hamcrest内联调用与拆分调用的编译差异
核心原因:Java泛型类型推断的场景差异
你遇到的问题本质是Java编译器在不同调用场景下,泛型类型推断的逻辑不同:
1. 拆分调用时的类型推断
当你单独声明变量并赋值:
Matcher<String> matcher = Matchers.is(m);
编译器会严格遵循局部类型约束:因为你显式指定了变量类型是Matcher<String>,所以会直接将Matchers.is的泛型<T>绑定为String,生成Matcher<String>实例。后续将这个实例传给assertThat时,assertThat的泛型<T>会被推断为JsonNode(因为第一个参数是JsonNode),但String并非JsonNode的父类型,Matcher<String>无法匹配Matcher<? super JsonNode>,因此编译失败。
2. 内联调用时的类型推断
当你将Matchers.is(m)直接作为参数传入assertThat时,编译器会进行全局类型推断,尝试找到一个能让整个调用链合法的泛型绑定:
assertThat的签名要求第二个参数是Matcher<? super T>,其中T是第一个参数的类型(或其父类型)- 编译器需要同时适配
Matchers.is(m)的返回类型:m是String类型,所以is的泛型<T>可以是String的任意父类型
此时编译器会选择最宽泛的兼容类型Object:
- 将
assertThat的泛型<T>绑定为Object,此时第二个参数要求是Matcher<? super Object>(即Matcher<Object>) - 将
Matchers.is(m)的泛型<T>也绑定为Object(因为String是Object的子类型,符合is方法的参数要求),返回Matcher<Object>,完全匹配assertThat的参数类型要求
这种全局推断会优先保证代码的合法性,因此编译通过,但运行时断言会失败(因为JsonNode和String不可能相等)。
内容的提问来源于stack exchange,提问作者frig.neutron
相关产品推荐
相关产品推荐

