我正在使用Java 8,并且对JUnit和AssertJ使用以下测试代码:
@Test
public void test() {
List<Number> actual = new ArrayList<>();
actual.add(Double.valueOf(1));
actual.add(Integer.valueOf(1));
actual.add(Long.valueOf(1));
List<Class<? extends Number>> expected = new ArrayList<>();
expected.add(Double.class);
expected.add(Integer.class);
expected.add(Long.class);
// Just information how IDEA is generating types for the different statements
ListAssert<? extends Class<? extends Number>> implicitLambda = assertThat(actual).extracting(t -> t.getClass());
ListAssert<Class<? extends Number>> explicitLambda = assertThat(actual).extracting((Extractor<Number, Class<? extends Number>>) t -> t.getClass());
ListAssert<Class<?>> methodReference = assertThat(actual).extracting(Number::getClass);
// Does not compile
// Error:(31, 84) java: incompatible types: java.util.List<java.lang.Class<? extends java.lang.Number>> cannot be converted to java.lang.Iterable<? extends java.lang.Class<capture#1 of ? extends java.lang.Number>>
assertThat(actual).extracting(t -> t.getClass()).containsExactlyElementsOf(expected);
// Compile because of explicit types
assertThat(actual).extracting((Extractor<Number, Class<? extends Number>>) t -> t.getClass()).containsExactlyElementsOf(expected);
// Compile, but don't understand why
assertThat(actual).extracting(Number::getClass).containsExactlyElementsOf(expected);
}
在第一个断言中,我遇到了一个编译错误:
Error:(31, 84) java: incompatible types: java.util.List<java.lang.Class<? extends java.lang.Number>> cannot be converted to java.lang.Iterable<? extends java.lang.Class<capture#1 of ? extends java.lang.Number>>
我有以下问题:
<? extends Class<? extends Number>
。The actual result type is Class<? extends |X|> where |X| is the erasure of the static type of the expression on which getClass is called.
Class<? extends Number>
。Class<?>
,那么我理解了,但是为什么返回类型与此隐式lambda不同?相关的AssertJ文档:
AbstractIterableAssert
AbstractIterableAssert.html#extracting
AbstractIterableAssert.html#containsExactlyElementsOf
在Java-8类型中,比在以前的Java版本中进行推理更棘手。特别是,现在表达式的类型可能取决于周围的上下文,而不仅取决于表达式本身。当你写
ListAssert<? extends Class<? extends Number>> implicitLambda = assertThat(actual).extracting(t -> t.getClass());
您提供了这样的上下文。您还可以将此表达式分配给其他类型:
ListAssert<Class<?>> implicitLambda2 = assertThat(actual).extracting(t -> t.getClass());
这也有效。请注意,您无法分配implicitLambda
给implicitLambda2
。您都不能分配implicitLambda2
给implicitLambda
。这两种类型无关。因此事实是:表达式本身可能没有特定的类型,它仅具有一组约束,这些约束可以根据周围的上下文来解决。通常,在将表达式分配,强制转换或传递给另一个方法时,将发生约束解析。JLS第18章对此进行了介绍,尽管很难阅读。
在链接调用中,没有合适的周围环境来明确地解析约束,因此,可能以与链中的下一个调用不兼容的方式解析类型(从不考虑链接调用来绑定约束)。您已经找到两种消除歧义的方法。一种是使用方法参考。这会有所帮助,因为对函数接口(由JLS 15.13.2覆盖)的映射方法引用与将lambda映射到函数接口(由JLS 15.27.3覆盖)完全不同。在这里,它有助于设置更具体的约束。另一个是明确指定lambda参数。JLS 15.27.3中的以下语句涵盖了这一点:
如果lambda表达式是显式键入的,则其形式参数类型与函数类型的参数类型相同。
因此,此处执行了更简单的类型解析过程。还有另一种方法:指定显式泛型参数:
// compiles fine
assertThat(actual).<Class<? extends Number>>extracting(t -> t.getClass()).containsExactlyElementsOf(expected);
// also compiles
assertThat(actual).<Class<?>>extracting(t -> t.getClass()).containsExactlyElementsOf(expected);
有时这是最短的方法。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句