为什么将一个参数的方法引用作为预期类型为BiConsumer
的参数(其抽象方法需要两个参数)传递是合法的?
示例:
class Experiment {
private String name;
public Experiment(String name) {
this.name = name;
}
public void oneParamMethod(Object o) {
System.out.println(this.name + " and " + o);
}
public <T, S> void executeBiConsumer(BiConsumer<T, S> biCon, T in1, S in2) {
biCon.accept(in1, in2);
}
public static void main(String[] args) {
// notice that the name is "INSTANCE", but it won't be printed out
Experiment exp = new Experiment("INSTANCE");
// executeBiConsumer expects a functional of two params but is given a method
// reference of one param. HOW IS THIS LEGAL?
exp.executeBiConsumer(Experiment::oneParamMethod, new Experiment("PARAM"), 999);
}
}
输出:
PARAM and 999
让我们更改调用,以使第二个参数不是 Experiment
的实例,如下所示:
exp.executeBiConsumer(Experiment::oneParamMethod, new String("INVALID"), 999);
现在,它不会编译。
Experiment
实例,为什么代码不会抱怨而编译,为什么不编译呢?BiConsumer
的参数是有效的?答案 0 :(得分:6)
引用具有一个参数的实例方法的方法引用实际上有两个参数-第一个参数是隐式的-在其上执行该方法的实例。
Experiment::oneParamMethod
等效于(Experiment e, Object o) -> e.oneParamMethod(o)
。
您要传递给BiConsumer<T, S>
的{{1}}是executeBiConsumer
,这意味着它必须接收BiConsumer<Experiment,Object>
的实例作为Experiment
的第一个参数方法。
因此
accept
有效,但是
exp.executeBiConsumer(Experiment::oneParamMethod, new Experiment("PARAM"), 999);
不是。
以下是相关的JLS参考(15.13.1):
第二,给定具有n个参数的目标函数类型,确定一组可能适用的方法:
如果方法引用表达式的形式为ReferenceType :: :: [TypeArguments] Identifier,则可能适用的方法是具有适当名称(由Identifier赋予),可访问性,友善性(n或n)的要搜索类型的成员方法。 -1),然后键入参数arity(源自[TypeArguments]),如§15.12.2.1中所述。
考虑了两个不同的变量n和n-1,以说明此形式引用静态方法还是实例方法的可能性。
您的目标函数类型-exp.executeBiConsumer(Experiment::oneParamMethod, new String("INVALID"), 999);
-有2个参数。因此,可能适用的方法是具有适当名称(BiConsumer
)和Arity 2或1(即1或2个自变量)的搜索类型(Experiment
)的成员方法。这包括您的oneParamMethod
方法。
答案 1 :(得分:4)
方法引用有四种
您正在使用的那个属于第三类。如果使用lambda代替方法引用,则可以更好地看到这一点。
您正在做什么
BiConsumer<Experiment, Integer> biCon = (experiment, someInt) ->
experiment.oneParamMethod(someInt)
第一个参数变为object
,在其上调用oneParamMethod
。上面的方法参考等同于您使用的-Experiment::oneParamMethod
。
如果您要转换oneParamMethod
静态,则会出现错误,因为Class::staticMethod
的lambda形式是将参数直接传递给static方法。
看起来像
BiConsumer<Experiment, Integer> biCon = (experiment, someInt) ->
Experiment.oneParamMethod(experiment, someInt)
并且您没有使用两个参数的oneParamMethod
方法。
参考:https://docs.oracle.com/javase/tutorial/java/javaOO/methodreferences.html
啊,我对第三种描述有疑问。
这听起来并不复杂。当我们使用Class::instanceMethod
时,大多数时候我们都会使用stream..filter..map
。假设我们要过滤年龄超过18岁的Person
对象。
persons.stream()
.filter(person -> person.getAge() > 18)
.map(person -> person.getName()) //Get only name
...
在这里,person -> person.getName()
可以写为Person::getName
。这与第三类相同。第一个隐式参数是特定类型的任意对象,而getName
是 instance方法。
希望这会有所帮助