让我们将其分解为尝试(粗略的尝试),以探索编译器发出错误的原因。
1. 与本语境的区别andThen
compose
以下是两种方法的签名(省略):default
<V> Function<T, V> andThen(Function<? super R, ? extends V> after)
<V> Function<V, R> compose(Function<? super V, ? extends T> before)
请记住,这是用两个类型变量声明的,在此上下文中,和 之间最显著的区别是期望采用数据类型为 的当前函数的返回类型,而 参数的数据类型是 的局部类型变量,编译器必须推断。Function
<T, R>
after
before
after
R
before
compose
V
为什么这与此相关?
2. 为什么有效?test.andThen(String::toUpperCase)
在编译器尝试执行的许多操作中,它试图验证这是 和 的有效参数。为此,它需要建立 的类型,这意味着它需要在该调用上下文中推断方法引用的数据类型。好吧,编译器知道 它必须是 一个 ,所以它只需要推断 的类型参数,这就是上述差异适用的地方:String::toUpperCase
andThen
compose
String::toUpperCase
String::toUpperCase
Function
Function
- 在 (参数 的 参数 ) 的情况下,的第一个参数已知是 (粗略地),它必须与当前函数的 ('s) 返回类型相同。第二个类型参数 ,也对应于 的局部类型变量,被推断为将声明返回的方法引用。
after
andThen
Function<? super R, ? extends V>
R
test
V
andThen
- 在 (参数 ) 的情况下,第一个参数的类型是未知的,必须在上下文中推断。它对应于 的类型变量 。这是第一个区别所在。
before
compose
compose
V
在 Eclipse 中,编译器错误为 ,这给出了错误与 匹配 的参数类型相关的线索。
现在,如果您看到上面的内容:The type String does not define toUpperCase(Object) that is applicable here
String#toUpperCase
-
after
是一个采用参数的函数,这使得编译器解析目标方法变得有些简单。这本身就是一个巨大的主题,但它归结为编译器选择将函数大致链接为lambda表达式 。方法引用可以解析为这样的实例方法,其中函数的第一个参数成为方法调用的目标(在 的情况下没有参数)。Function
String
String
String::toUpperCase
(String s) -> s.toUpperCase()
Function<T, U>
-
before
可以以相同的方式解决,除了编译器不确定的第一个类型参数的类型是什么。而且由于重载,编译器不能只是决定将其推断为 ,因为可能意味着数据类型将强制它解析对重载的方法引用。换句话说,编译器正在处理先有鸡还是先有蛋的情况(它需要知道参数类型来决定链接哪种方法,但不能使用签名来推断,因为有两种可能性使它变得模糊)。Function
String#toUpperCase
(String s) -> s.toUpperCase()
V
String.toUpperCase(Locale)
V
String#toUpperCase
String#toUpperCase
V
3. 您如何解决
您通过强制转换为 来编译此代码。这是做什么的?它只是让编译器摆脱了上述困境:你告诉编译器这是,不是或任何其他可能性,这导致编译器将方法引用解析为(没有留下任何空间成为有效的选项)String::toUpperCase
Function<String, String>
V
String
Locale
(String s) -> s.toUpperCase()
String.toUpperCase(Locale)
4.其他解决方法
您可以通过为 的 强制显式类型参数来有效地执行相同的操作:before
V
-
test.compose((String s) -> s.toUpperCase()).apply("bar");
将编译,因为显式键入 的参数的数据类型(String s)
before
-
test.<String>compose(String::toUpperCase).apply("bar");
通过帮助编译器的推理逻辑来做同样的事情,告诉它是一个 ,这避免了上面提到的先有鸡还是先有蛋的情况。V
String
这种类型的解决方案并不是一些奇怪的方法,你基本上在做与编译器拒绝时强制转换相同的事情,尽管这些方法包括泛型作为香料。System.out.println((String)null)
System.out.println(null)
Function