Guava Futures 中不明确的 lambda 超载
Ambiguous lambda overload in Guava Futures
我相信我遇到了 eclipse bug,但我想确认一下。
我正在使用 java 8 (jdk 1.8.0_102),我的代码编译正常,但 eclipse 给我一个错误。
我的代码如下所示:
public ListenableFuture<ProtoBufExchange> myMethod(
//some code here
return Futures.transform(future,(Request.Builder reqBuilder) -> {
//some code here
return Futures.immediateFuture(exchange);
}
eclipse 中显示的错误是这样的:
The method transform(ListenableFuture<Request.Builder>, AsyncFunction<? super Request.Builder,? extends
ProtoBufExchange>) is ambiguous for the type Futures.
如果我进行强制转换,eclipse 不会报错:
public ListenableFuture<ProtoBufExchange> myMethod(
//some code here
return Futures.transform(future,(AsyncFunction<Request.Builder, ProtoBufExchange>) (Request.Builder reqBuilder) -> {
//some code here
return Futures.immediateFuture(exchange);
}
我知道 guava 15.0 Future.transform() 被重载,具有以下两种形式(在较新的 guava 版本上,异步方法具有不同的名称):
transform(ListenableFuture<I> input, Function<? super I,? extends O> function)
或
transform(ListenableFuture<I> input, AsyncFunction<? super I,? extends O> function)
但是 jdk 编译器以某种方式解决了这种歧义。可能是因为在上面的代码中,如果我们实现的是函数而不是 AsyncFunction,Futures.transform 的 return 类型将与方法 return 类型不匹配。
是eclipse的bug吗?我在这里错过了什么吗?
有关我的环境的更多详细信息:
jdk: 1.8.0_102
日食:4.6.2
番石榴:15.0
“显式类型的 lambda 表达式”和“隐式类型的 lambda 表达式”是有区别的。
name -> expressiorOrBlock
或 (name[,name]*) -> expressionOrBlock
形式的隐式类型 lambda 表达式需要其上下文(即已解析的方法)来确定其类型,因此不用于消除重载方法的歧义。这样做并非不可能,但由于由此产生的复杂性,规范明确排除了它。
形式为 (Type name[, Type name]*) -> expressionOrBlock
的显式类型 lambda 表达式具有确定其功能签名所需的一切,包括其 return 类型,这允许使用它们来消除重载方法的歧义。
举个简单的例子:
interface F<T,R> {
R apply(T t);
}
interface AF<T,R> {
Future<R> apply(T t);
}
static <T,R> Future<R> method(F<T,R> f) {
return null;
}
static <T,R> Future<R> method(AF<T,R> f) {
return null;
}
public static void main(String[] args) {
// these two don't compile
method(x -> "bla");
method(x -> new FutureTask<String>(null));
// these two do compile
method((Object x) -> "bla");
method((Object x) -> new FutureTask<String>(null));
}
正式规则在JLS, §15.12.2.5. Choosing the Most Specific Method中指定,但它也包含一个非正式提示:
The informal intuition is that one method is more specific than another if any invocation handled by the first method could be passed on to the other one without a compile-time error.
我认为,很容易看出每个可以由 method(AF)
处理的调用也可以由 method(F)
处理,而相反的情况不适用,即 method((Object x) -> "bla")
调用只能由 method(F)
处理,因此它没有歧义,而 method((Object x) -> new FutureTask<String>(null))
可以由两者处理,但 method(AF)
更具体 。
A functional interface type S
is more specific than a functional interface type T
for an expression e
if T
is not a subtype of S
and one of the following is true (where U₁
... U<sub>k</sub>
and R₁
are the parameter types and return type of the function type of the capture of S
, and V₁
... V<sub>k</sub>
and R₂
are the parameter types and return type of the function type of T
):
- If e is an explicitly typed lambda expression (§15.27.1), then one of the following is true:
R₂
is void
.
R₁
<:
R₂
.
- …
所以在这个涉及显式类型的 lambda 表达式的特定情况下,函数类型的 return 类型已经足以消除歧义。
但请注意,编译示例代码也会产生警告:
warning: [overloads] <T#1,R#1>method(F<T#1,R#1>)
… is potentially ambiguous with <T#2,R#2>method(AF<T#2,R#2>)
提醒隐式类型的 lambda 表达式对于此类重载方法总是不明确的,并且使用显式类型 lambda 表达式的方法选择可能不直观。
我相信我遇到了 eclipse bug,但我想确认一下。
我正在使用 java 8 (jdk 1.8.0_102),我的代码编译正常,但 eclipse 给我一个错误。
我的代码如下所示:
public ListenableFuture<ProtoBufExchange> myMethod(
//some code here
return Futures.transform(future,(Request.Builder reqBuilder) -> {
//some code here
return Futures.immediateFuture(exchange);
}
eclipse 中显示的错误是这样的:
The method transform(ListenableFuture<Request.Builder>, AsyncFunction<? super Request.Builder,? extends ProtoBufExchange>) is ambiguous for the type Futures.
如果我进行强制转换,eclipse 不会报错:
public ListenableFuture<ProtoBufExchange> myMethod(
//some code here
return Futures.transform(future,(AsyncFunction<Request.Builder, ProtoBufExchange>) (Request.Builder reqBuilder) -> {
//some code here
return Futures.immediateFuture(exchange);
}
我知道 guava 15.0 Future.transform() 被重载,具有以下两种形式(在较新的 guava 版本上,异步方法具有不同的名称):
transform(ListenableFuture<I> input, Function<? super I,? extends O> function)
或
transform(ListenableFuture<I> input, AsyncFunction<? super I,? extends O> function)
但是 jdk 编译器以某种方式解决了这种歧义。可能是因为在上面的代码中,如果我们实现的是函数而不是 AsyncFunction,Futures.transform 的 return 类型将与方法 return 类型不匹配。
是eclipse的bug吗?我在这里错过了什么吗?
有关我的环境的更多详细信息:
jdk: 1.8.0_102
日食:4.6.2
番石榴:15.0
“显式类型的 lambda 表达式”和“隐式类型的 lambda 表达式”是有区别的。
name -> expressiorOrBlock
或 (name[,name]*) -> expressionOrBlock
形式的隐式类型 lambda 表达式需要其上下文(即已解析的方法)来确定其类型,因此不用于消除重载方法的歧义。这样做并非不可能,但由于由此产生的复杂性,规范明确排除了它。
形式为 (Type name[, Type name]*) -> expressionOrBlock
的显式类型 lambda 表达式具有确定其功能签名所需的一切,包括其 return 类型,这允许使用它们来消除重载方法的歧义。
举个简单的例子:
interface F<T,R> {
R apply(T t);
}
interface AF<T,R> {
Future<R> apply(T t);
}
static <T,R> Future<R> method(F<T,R> f) {
return null;
}
static <T,R> Future<R> method(AF<T,R> f) {
return null;
}
public static void main(String[] args) {
// these two don't compile
method(x -> "bla");
method(x -> new FutureTask<String>(null));
// these two do compile
method((Object x) -> "bla");
method((Object x) -> new FutureTask<String>(null));
}
正式规则在JLS, §15.12.2.5. Choosing the Most Specific Method中指定,但它也包含一个非正式提示:
The informal intuition is that one method is more specific than another if any invocation handled by the first method could be passed on to the other one without a compile-time error.
我认为,很容易看出每个可以由 method(AF)
处理的调用也可以由 method(F)
处理,而相反的情况不适用,即 method((Object x) -> "bla")
调用只能由 method(F)
处理,因此它没有歧义,而 method((Object x) -> new FutureTask<String>(null))
可以由两者处理,但 method(AF)
更具体 。
A functional interface type
S
is more specific than a functional interface typeT
for an expressione
ifT
is not a subtype ofS
and one of the following is true (whereU₁
...U<sub>k</sub>
andR₁
are the parameter types and return type of the function type of the capture ofS
, andV₁
...V<sub>k</sub>
andR₂
are the parameter types and return type of the function type ofT
):
- If e is an explicitly typed lambda expression (§15.27.1), then one of the following is true:
R₂
isvoid
.R₁
<:
R₂
.- …
所以在这个涉及显式类型的 lambda 表达式的特定情况下,函数类型的 return 类型已经足以消除歧义。
但请注意,编译示例代码也会产生警告:
warning: [overloads]
<T#1,R#1>method(F<T#1,R#1>)
… is potentially ambiguous with<T#2,R#2>method(AF<T#2,R#2>)
提醒隐式类型的 lambda 表达式对于此类重载方法总是不明确的,并且使用显式类型 lambda 表达式的方法选择可能不直观。