【发布时间】:2015-03-07 15:09:29
【问题描述】:
我正在尝试编写一些代码来接收 java.util.function.* 包中类的各种实现,但我一直在针对我真正想要解决的特定语法(显式类型)运行某个编译器错误像obj.<Type>foo(/*...*/) 这样的论点有效,但它们缺乏优雅)。
简而言之,我希望能够始终使用方法引用,但由于某种原因,我不明白为什么编译器无法理解下面解释的引用。
假设以下两个类(实现无关):
一些带有getter/setter方法的实体类:
class Thing {
public List<String> getThings() {
return null;
}
public void setThings(List<String> things) {
// ...
}
}
对该实体类的实例执行操作的其他类:
class FooBar<A> {
public <B> void foo(Function<A, List<B>> f) {
// ...
}
public <B> void bar(BiConsumer<A, List<B>> f) {
// ...
}
public <B> void wuz(Function<A, List<B>> x, BiConsumer<A, List<B>> y) {
// ...
}
}
当执行对这些方法的调用时,编译器会给我各种错误:
// create an instance of the handler class
FooBar<Thing> fb = new FooBar<>();
例如。 1)调用期望函数的方法可以正常工作,没有编译错误:
fb.foo(Thing::getThings);
例如。 2)然而调用期望双消费者的方法给了我这个:
fb.bar(Thing::setThings);
// The type Thing does not define setThings(Thing, List<B>) that is applicable here
例如。 3)正如预期的那样,明确说明类型工作正常,没有编译错误:
fb.<String>bar(Thing::setThings);
例如。 4)然而,当我写出 lambda 时,它给了我一个不同的编译错误(虽然在 lambda 参数中说明类型工作正常):
fb.bar((thing, things) -> thing.setThings(things));
// The method setThings(List<String>) in the type Thing is not applicable for the arguments (List<Object>)
例如。 5)当调用期望两者的方法时,每个参数都会出现不同的编译错误:
fb.wuz(
Thing::getThings,
// The type of getThings() from the type Thing is List<String>, this is incompatible with the descriptor's return type: List<B>
Thing::setThings
// The type Thing does not define setThings(Thing, List<B>) that is applicable here
);
例如。 6) 再次,正如预期的那样,明确说明类型再次正常工作,没有编译错误:
fb.<String>wuz(Thing::getThings, Thing::setThings);
例如。 7)这是最让我感到困惑的:对于同时期望函数和双消费者的方法,写出只是双消费者(无类型)并使用函数的方法引用,工作正常,没有编译错误(?!):
fb.wuz(Thing::getThings, (thing, things) -> thing.setThings(things));
我不明白的是,编译器在确定不同场景下的运行时类型/类型擦除时,显然会以不同的方式处理显式 lambda 和方法引用,即:
- 获取参数并返回值的函数式接口
- 获取参数但不返回值的函数式接口
- 带有 1. 和 2. 类型参数的方法
这似乎只发生在函数接口的类型本身是泛型类型(在本例中为 List)时,这让我相信这与类型擦除有关,但我不知所措至于答案。
我希望能够写作......
fb.wuz(Thing::getThings, Thing::setThings);
...没有显式类型或普通 lambda 表达式。
如果有办法重构FooBar 中的方法来支持这一点,我真的很想知道。
当然,如果有人能够解释编译器的这些不同行为,我也非常感激! :-)
编辑
我特别有兴趣了解为什么示例 #1 和 #7 确实有效,但为什么示例 #4 本身不有效(然后,为什么示例 #2 也不起作用)。
【问题讨论】:
-
我不知道这是否有帮助,但在这里:
class FooBar<A, B>;从方法签名中删除 ;FooBar<Thing, String> fb = new FooBar<>(); -
这确实可以解决它,但这意味着 B 是为整个实例预定义的,而我真的需要它在每次调用该方法时都不同。我特别好奇为什么一个参数的 lambda 简写 不起作用 工作,而两个参数方法的 lambda 简写 确实 工作(我真的很想了解为什么)。
-
您使用哪个
jdk版本?使用我的,除了一个调用之外的所有调用都被编译而没有错误。我现在正在下载最新版本以验证结果。 -
我正在运行 jdk 1.8.0_25,但你让我思考。我手动编译了这个例子,就像你说的那样工作得很好!我进一步看了看,这似乎是 Eclipse 编辑器中的一个错误。我将 Eclipse 从 4.4.0 更新到 4.4.2,然后编辑器也工作得很好。非常感谢您帮助我意识到这一点,真不敢相信我没有早点想到。这不是 Eclipse 编辑器第一次让我花费数小时来解决问题,只是为了找出它是 Eclipse 中的一个错误!
标签: java generics lambda eclipse-luna method-reference