Java编译错误:方法引用与重载相结合
我有以下带有重载方法的类:Java编译错误:方法引用与重载相结合,java,java-8,compiler-errors,overloading,method-reference,Java,Java 8,Compiler Errors,Overloading,Method Reference,我有以下带有重载方法的类: import java.util.ArrayList; import java.util.concurrent.Callable; public abstract class Test { public void test1 () { doStuff (ArrayList::new); // compilation error } public void test2 () { doStuff ( () -> new ArrayL
import java.util.ArrayList;
import java.util.concurrent.Callable;
public abstract class Test {
public void test1 () {
doStuff (ArrayList::new); // compilation error
}
public void test2 () {
doStuff ( () -> new ArrayList<> ());
}
public abstract void doStuff (Runnable runable);
public abstract void doStuff (Callable<ArrayList<String>> callable);
}
这里执行方法doStuff(Runnable)
,这是显而易见的
但是编译器如何决定在test2
中执行这两个方法中的哪一个呢
为什么我可以使用lambda表达式而不是方法引用
test2
中的lambda表达式使用返回可调用的方法,为什么方法引用尝试使用其他方法
在我看来,这就像一个java bug
编辑:
它与ArrayList
和/或它的泛型类型无关。当您有可调用的
或任何其他对象时,也会出现相同的错误
提前谢谢
迪米特里
但是编译器如何决定在test2中执行这两个方法中的哪一个呢
public void test2(){
doStuff(()->newarraylist());
}
含蓄地
public void test2 () {
doStuff ( () -> { return new ArrayList<>(); } );
}
public void test2(){
doStuff(()->{returnnewarraylist();});
}
并且只有Callable返回一个对象
为什么我可以使用lambda表达式而不是方法引用
可能是
public void test2 () {
doStuff ( () -> { new ArrayList<>(); } );
}
public void test2(){
doStuff(()->{newarraylist();});
}
已编辑
看看这些例子:
Runnable r = ArrayList::new; // compiled
Callable c = ArrayList::new; // compiled
doStuff(ArrayList::new); // compile error, ambiguous
因此ArrayList::new
被解释为Runnable
和Callable
。注意,不涉及lambda
下一个例子:
Runnable r = () -> new ArrayList<>(); // compiled
Callable c = () -> new ArrayList<>(); // compiled
doStuff(() -> new ArrayList<>()); // compiled, the Callable one
Runnable r=()->new ArrayList();//汇编
可调用的c=()->new ArrayList();//汇编
doStuff(()->new ArrayList());//编译的,可调用的
将()->new ArrayList()
传递给方法时
() -> { return new ArrayList<>(); }
()->{返回新的ArrayList();}
优先于
() -> { new ArrayList<>(); }
()->{new ArrayList();}
因此,调用了
可调用的
一个,没有任何歧义。好吧,我们可以简化这一点:
// takes a Runnable
public static void doStuff(Runnable runable) {
System.out.println("Runnable");
}
// takes a Callable
public static void doStuff(Callable<List<String>> callable) {
System.out.println("Callable");
}
嗯。。。这将无法匹配。你可能会认为这是愚蠢的,因为只有当go
是一个不带参数的函数时才有意义,在这种简单的情况下,你很容易做出这样的判断,这不适合编译器。本质上,这就像一把死锁:
为了知道调用哪个doStuff
方法,我们需要知道调用哪个go
;同时,为了了解要调用哪个go
,我们需要知道要调用哪个doStuff
,或者:
为了找到目标类型,我们需要解析方法,但是为了解析方法,我们需要知道目标类型
对于
ArrayList
具有多个构造函数的情况,也会发生同样的情况 是的,你是对的。ArrayList::new
可能意味着这两件事。但是为什么新的ArrayList()可以工作呢?这也可能意味着两件事。如果我删除了doStuff(Callable)
的声明,那么test2
将使用另一种方法。那么为什么这不是一个编译错误呢。如果这两种情况下的行为不一致,@Dimitrios Begnis否,()->new ArrayList()
与()->{return new ArrayList();}
相同,它不是()->{new ArrayList();}
。只有那些带有一行return语句的lambda才能省略方括号和return
关键字。@zhh那么为什么Runnable r=()->new ArrayList()
编译没有任何问题,但是Runnable r2=()->{return new ArrayList();}
不会,如果您声称两者“相同”?@Dimitrios Begnis您不能说Runnable
比Callable
更可取,反之亦然。但是您可以说()->{return new ArrayList();}
比()->{new ArrayList();}
更可取,因为它们都是lambda。“方法引用是[…]易于阅读的lambda表达式[…]”,因此我们更容易理解方法引用。但我认为编译器将方法引用和lambda视为两件事。@zhh再次强调,在这个简单的例子中,这对您来说很容易,而不是编译器,因为它可能是100*100选项。他们必须在某个地方进行剪切,因此,他们没有为最多5种方法提供“resolution will work”之类的内容,而是将其完全剪切。不,这是错误的,就像我在对另一个答案的评论中所说的那样,当时我只有doStuff(Runnable)
test2方法编译并使用test2
。因此,在这种情况下,它不能返回任何内容,否则当您有一个doStuff
调用时,它将导致编译错误。单独尝试一下doStuff(Callable)
,你也不会出错。是的,我知道,我明白你的意思。但我想知道为什么编译器可以决定使用lambda表达式的方法,但不能使用方法引用来决定?因此,lambdas的规则似乎是将单行表达式编译为带有return
的语句,但当需要runnable时,表达式将遵从不带return
的方法。为什么方法引用没有这样的顺序?@PeterLawrey这是错误的,原因是不同的:@PeterLawrey我同意Eugene-如果您将ArrayList::new
替换为可以明确解析的方法引用(例如doStuff(Object::new);
),并将重载调整为doStuff(可调用可调用)
,此重载明确更具体,将被选择。问题是ArrayList::new
方法引用可能引用多个构造函数。可能相关:不要重载方法以在同一参数位置使用不同的函数接口,因为这会导致混淆。本领域的最新讨论ld:(引用约书亚·布洛赫(Joshua Bloch)的一句著名的话)。@StephanHerrmann我认为这与这个问题无关,请看我的答案@
() -> { new ArrayList<>(); }
// takes a Runnable
public static void doStuff(Runnable runable) {
System.out.println("Runnable");
}
// takes a Callable
public static void doStuff(Callable<List<String>> callable) {
System.out.println("Callable");
}
private static List<String> go() {
return null;
}
private static List<String> go(int i) {
return null;
}
doStuff(YourClass::go);