一个参数的BiConsumer和方法参考

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)

现在,它将无法编译。


  1. 如果第二个参数是Experiment实例,为什么代码不会抱怨而编译,为什么不编译呢?
  2. 为什么只传递一个参数的方法引用作为期望的参数是有效的BiConsumer

Era*_*ran 6

引用具有一个参数的实例方法的方法引用实际上具有两个参数-第一个参数是隐式的-在其上执行该方法的实例。

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)方法。


use*_*er7 5

添加Eran 的回答

有四种方法引用

  1. 引用静态方法
  2. 对特定对象的实例方法的引用
  3. 对特定类型的任意对象的实例方法的引用
  4. 对构造函数的引用

您正在使用的属于第三类。如果我们使用 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采用两个参数的方法。

参考:

Oracle 方法参考

方法参考


啊,我对第三种的描述有问题。

这并不像听起来那么复杂。我们使用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实例方法

希望这可以帮助