Nik*_*mbt 3 java this java-8 functional-interface method-reference
为什么将一个参数的方法引用作为期望类型的参数(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);
}
}
Run Code Online (Sandbox Code Playgroud)
输出:
PARAM and 999
Run Code Online (Sandbox Code Playgroud)
让我们更改调用,使第二个参数不是Experiment如下的实例:
exp.executeBiConsumer(Experiment::oneParamMethod, new String("INVALID"), 999);
Run Code Online (Sandbox Code Playgroud)
现在,它将无法编译。
Experiment实例,为什么代码不会抱怨而编译,为什么不编译呢?BiConsumer?引用具有一个参数的实例方法的方法引用实际上具有两个参数-第一个参数是隐式的-在其上执行该方法的实例。
Experiment::oneParamMethod等同于(Experiment e, Object o) -> e.oneParamMethod(o)。
在BiConsumer<T, S>要传递到executeBiConsumer是一个BiConsumer<Experiment,Object>,这意味着它必须接收一个实例的Experiment作为的第一个参数accept的方法。
因此
exp.executeBiConsumer(Experiment::oneParamMethod, new Experiment("PARAM"), 999);
Run Code Online (Sandbox Code Playgroud)
是有效的,但是
exp.executeBiConsumer(Experiment::oneParamMethod, new String("INVALID"), 999);
Run Code Online (Sandbox Code Playgroud)
不是。
这是相关的JLS参考(15.13.1):
其次,给定具有n个参数的目标函数类型,确定一组潜在适用的方法:
如果方法引用表达式的形式为ReferenceType :: [TypeArguments] Identifier,则可能适用的方法是具有适当名称(由Identifier赋予),可访问性,一致性(n或n-1)的要搜索类型的成员方法。 ,然后输入参数arity(源自[TypeArguments]),如§15.12.2.1中所指定。
考虑了两个不同的变量n和n-1,以说明这种形式是引用静态方法还是实例方法的可能性。
您的目标函数类型BiConsumer--有2个参数。因此,潜在适用的方法是Experiment具有适当名称(oneParamMethod)和2或1(即1或2个自变量)的搜索()类型的成员方法。这包括您的public void oneParamMethod(Object o)方法。
添加Eran 的回答
有四种方法引用
您正在使用的属于第三类。如果我们使用 lambda 替换方法引用,我们可以更好地看到这一点。
你正在做的是
BiConsumer<Experiment, Integer> biCon = (experiment, someInt) ->
experiment.oneParamMethod(someInt)
Run Code Online (Sandbox Code Playgroud)
第一个参数将成为object在其上oneParamMethod被调用。与上述等效的方法引用是您使用的 - Experiment::oneParamMethod。
如果要转换oneParamMethod静态,则会收到错误,因为Class::staticMethod的 lambda 形式将按原样将参数传递给静态方法。
它看起来像
BiConsumer<Experiment, Integer> biCon = (experiment, someInt) ->
Experiment.oneParamMethod(experiment, someInt)
Run Code Online (Sandbox Code Playgroud)
并且您没有oneParamMethod采用两个参数的方法。
参考:
啊,我对第三种的描述有问题。
这并不像听起来那么复杂。我们使用Class::instanceMethod的大部分时间,当我们使用stream..filter..map。假设我们要过滤Person年龄超过 18 岁的对象。
persons.stream()
.filter(person -> person.getAge() > 18)
.map(person -> person.getName()) //Get only name
...
Run Code Online (Sandbox Code Playgroud)
这里,person -> person.getName()可以写成Person::getName。这与第三类情况相同。第一内隐参数是特定类型的任意对象和getName为实例方法。
希望这可以帮助