BiConsumer和一个参数的方法参考

BiConsumer and method reference of one parameter

为什么将一个参数的方法引用作为预期类型的​​参数传递是合法的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);

现在,它无法编译。


  1. 如果第二个参数是 Experiment 实例,为什么代码编译没有报错,否则为什么不编译?
  2. 为什么将只有一个参数的方法引用作为需要 BiConsumer 的参数传递是有效的?

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

Experiment::oneParamMethod 等价于 (Experiment e, Object o) -> e.oneParamMethod(o).

您传递给 executeBiConsumerBiConsumer<T, S> 是一个 BiConsumer<Experiment,Object>,这意味着它必须接收 Experiment 的实例作为 [=18] 的第一个参数=]方法。

因此

exp.executeBiConsumer(Experiment::oneParamMethod, new Experiment("PARAM"), 999);

有效,但是

exp.executeBiConsumer(Experiment::oneParamMethod, new String("INVALID"), 999);

不是。

这是相关的 JLS 参考资料 (15.13.1):

Second, given a targeted function type with n parameters, a set of potentially applicable methods is identified:

If the method reference expression has the form ReferenceType :: [TypeArguments] Identifier, the potentially applicable methods are the member methods of the type to search that have an appropriate name (given by Identifier), accessibility, arity (n or n-1), and type argument arity (derived from [TypeArguments]), as specified in §15.12.2.1.

Two different arities, n and n-1, are considered, to account for the possibility that this form refers to either a static method or an instance method.

您的目标函数类型 - BiConsumer - 有 2 个参数。因此,可能适用的方法是要搜索的类型 (Experiment) 的成员方法,它们具有适当的名称 (oneParamMethod) 和 arity 2 或 1(即 1 或 2 个参数)。这包括您的 public void oneParamMethod(Object o) 方法。

加上

有四种方法引用

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

您使用的属于第三类。如果我们使用 lambda 替换方法引用,我们可以更好地看到这一点。

你正在做的是

BiConsumer<Experiment, Integer> biCon = (experiment, someInt) -> 
            experiment.oneParamMethod(someInt)

第一个参数成为调用 oneParamMethodobject。与上述等效的方法参考是您使用的 - Experiment::oneParamMethod.


如果您要将 oneParamMethod 转换为静态,您会得到一个错误,因为 Class::staticMethod 的 lambda 形式会将参数原样传递给静态方法。

看起来像

BiConsumer<Experiment, Integer> biCon = (experiment, someInt) -> 
        Experiment.oneParamMethod(experiment, someInt)

而且您没有 oneParamMethod 方法接受两个参数。

参考文献:

Oracle Method reference

Method References


Ah, I have a problem with the description of the third kind.

这并不像听起来那么复杂。当我们使用stream..filter..map时,我们大部分时间都使用Class::instanceMethod。比方说,我们要过滤 Person 年龄超过 18 岁的对象。

persons.stream()
       .filter(person -> person.getAge() > 18)
       .map(person -> person.getName()) //Get only name
       ...

这里,person -> person.getName()可以写成Person::getName。这与第 3 类情况相同。第一个 implict 参数是 特定类型的任意对象 getName 实例方法 .

希望这对您有所帮助