使用隐式 lambda 时出现不兼容类型错误
Incompatible types error when using implicit lambda
我正在使用 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>>
我有以下问题:
- 第一个断言中lambda的return类型真的是
<? extends Class<? extends Number>
.
我只是依赖IDEA生成的局部变量。
- 如果是,那是为什么呢? getClass 文档说
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>
.
- 我应该使用哪个工具来查看隐式 lambda 的类型?我可以使用 JDK?
中的一些工具来检查它吗
- 为什么最后一个带有方法引用的断言编译通过了?如果它真的 return
Class<?>
,那么我明白了,但为什么 return 类型与此和隐式 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 chapter 18 对此进行了介绍,尽管它很难阅读。
在链接调用中,没有合适的周围上下文来明确地解析约束,因此可能会以与链中的下一个调用不兼容的方式解析类型(从不考虑链接调用来绑定约束) .您正确地找到了两种消除歧义的方法。一种是使用方法参考。这有帮助,因为映射方法引用到功能接口(由 JLS 15.13.2) is completely different procedure from mapping lambda to the functional interface (covered by JLS 15.27.3 涵盖);在这里它有助于设置更具体的约束。另一个是明确指定 lambda 参数。它包含在 JLS 15.27.3 中的以下语句中:
If the lambda expression is explicitly typed, its formal parameter types are the same as the parameter types of the function type.
所以这里执行更简单的类型解析过程。还有另一种方法:指定显式泛型参数:
// compiles fine
assertThat(actual).<Class<? extends Number>>extracting(t -> t.getClass()).containsExactlyElementsOf(expected);
// also compiles
assertThat(actual).<Class<?>>extracting(t -> t.getClass()).containsExactlyElementsOf(expected);
有时这是最短路线。
我正在使用 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>>
我有以下问题:
- 第一个断言中lambda的return类型真的是
<? extends Class<? extends Number>
.
我只是依赖IDEA生成的局部变量。 - 如果是,那是为什么呢? getClass 文档说
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>
. - 我应该使用哪个工具来查看隐式 lambda 的类型?我可以使用 JDK? 中的一些工具来检查它吗
- 为什么最后一个带有方法引用的断言编译通过了?如果它真的 return
Class<?>
,那么我明白了,但为什么 return 类型与此和隐式 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 chapter 18 对此进行了介绍,尽管它很难阅读。
在链接调用中,没有合适的周围上下文来明确地解析约束,因此可能会以与链中的下一个调用不兼容的方式解析类型(从不考虑链接调用来绑定约束) .您正确地找到了两种消除歧义的方法。一种是使用方法参考。这有帮助,因为映射方法引用到功能接口(由 JLS 15.13.2) is completely different procedure from mapping lambda to the functional interface (covered by JLS 15.27.3 涵盖);在这里它有助于设置更具体的约束。另一个是明确指定 lambda 参数。它包含在 JLS 15.27.3 中的以下语句中:
If the lambda expression is explicitly typed, its formal parameter types are the same as the parameter types of the function type.
所以这里执行更简单的类型解析过程。还有另一种方法:指定显式泛型参数:
// compiles fine
assertThat(actual).<Class<? extends Number>>extracting(t -> t.getClass()).containsExactlyElementsOf(expected);
// also compiles
assertThat(actual).<Class<?>>extracting(t -> t.getClass()).containsExactlyElementsOf(expected);
有时这是最短路线。