关于Java方法引用中隐式类参数的问题

问题描述 投票:0回答:2

在Java中,方法引用的类部分可以成为函数的第一个参数。我在工作代码中看到了很多示例。但我想知道 Java 语言规范中的哪里对此进行了详细说明?我很想知道,因为每当我看到这样的代码时,我都会花几分钟的时间来弄清楚为什么有一个“额外的”第一个参数。所以我认为阅读实际规则会有所帮助。函数式接口或者方法引用的章节里好像没有提到。

这是一个例子。

import java.util.function.BiFunction;

public class TestBiFunction
{
    public static void main(String[] args) {
        BiFunction<A.B, Integer, Integer> fx = A.B::dbl;
        A.B b = new A.B();
        int result = fx.apply(b, 3);
        System.out.println("Result = " + result);
    }

    private static class A {
        private static class B {
            public int dbl(int x) {
                return 2 * x;
            }
        }
    }
}

那么,我具体想知道的是,在语句“BiFunction fx = A.B::dbl;”中,什么语言规则使得“A.B::dbl”中的“A.B”成为 BiFunction 的第一个参数?

java functional-programming implicit
2个回答
2
投票

在 Java 语言规范中很难找到这一点,但我认为您正在寻找 第 15.13.3 节,至少在规范的第 8 版中,特别是该行

如果编译时声明是实例方法,则目标引用是调用方法的第一个形参。否则就没有目标参考。


0
投票

正如@LouisWasserman的回答提到的,它是§15.13.3

一种有用的思考方式:

receiver.method(arg1, arg2);

这和:

之间没有任何有意义的区别吗?
TypeOfReceiver.method(receiver, arg1, arg2);

除了动态分派(其中在编译时表达式的类型是相关的,但在运行时,您通过引用找到的实际对象,如果它是覆盖该方法的更具体类型,则覆盖的调用实现 - 这是实例方法所独有的,而不是静态方法所获得的)。

事实上,在 JVM 级别,它实际上或多或少是这样工作的,您甚至可以将它添加到方法定义中!这是合法的java:

public class MyExample {
  void instanceMethod(MyExample this, int param1) {
  }
}

instanceMethod
是一个带有 1 (!) 个参数 (param1) 的实例方法。您可以使用以下方式调用:
new MyExample().instanceMethod(5)
MyExample this
完全被忽略,但合法。它的存在是为了让您可以在“接收者”上添加注释。

当考虑这个假设的实例方法时,它确实有 2 个参数。 JVM 实现将在调用之前将 2 个东西压入堆栈:

this
引用和
param1
值。

同样的想法也适用于 lambda。给定 just “嘿,我正在谈论 instanceMethod 作为一种方法”,那么它确实有 2 个参数。

Java 确实允许部分绑定的概念,不过 - 您可以选择提供一半的参数列表(具体来说,

this
参考):

MyExample myEx = new MyExample();
IntConsumer ic = myEx::instanceMethod;
ic.accept(5);

有效 - 最后一行与调用

myEx.instanceMethod(5)
相同。
myEx
引用“烘焙”到
ic
,因此这就是它的来源,而“5”是通过调用
accept
提供的。

对比:

MyExample myEx = new MyExample();
BiConsumer<MyExample, Integer> tif = MyExample::instanceMethod;
tif.accept(myEx, 5);

(没有 BiIntConsumer 原始变体,因此我们必须求助于自动装箱才能使其工作) - 这里我们的

tif
函数没有预先绑定参数,因此需要提供 both。毕竟,如果没有
instanceMethod
的实例,就不可能调用
MyExample
。那么
tif.accept(5)
将如何工作 -
this
instanceMethod
代码的上下文中会包含什么?

因此,我的建议是 - 除了专门阅读有关lambda的规范之外,请考虑java中的每个非静态方法作为第一个参数,

TypeThisIsIn this
,即使你不写它。你可以写下,如果你愿意,编译器甚至会让你这么做。

© www.soinside.com 2019 - 2024. All rights reserved.