Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/315.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java Lambda表达式:对特定对象的实例方法的引用_Java_Lambda_Java 8 - Fatal编程技术网

Java Lambda表达式:对特定对象的实例方法的引用

Java Lambda表达式:对特定对象的实例方法的引用,java,lambda,java-8,Java,Lambda,Java 8,此代码使用方法引用特定对象的实例方法: public class Main { public static void main(String[] args) { One one=new One(); // F f = ()->{one.bar();}; //previous wrong syntax F f = one::bar; //4 f.foo(); } } class O

此代码使用方法引用特定对象的实例方法:

public class Main {
    public static void main(String[] args) {
        One one=new One();
        // F f = ()->{one.bar();};   //previous wrong syntax
        F f = one::bar;              //4
        f.foo();
    }
}

class One{void bar(){}}
interface F{void foo();}
我知道这很有效。但我无法理解为什么和如何

我无法理解的是,
F.foo()
方法如何可能使用对对象的引用,而该对象不是方法本身的参数(签名不是
void foo(One)

我在四号线

  • 创建实现
    F
    接口的类的实例
  • 通过使用引用
    one
    调用
    bar()
    方法来实现该方法

  • 但是
    foo()
    如何在
    one
    引用上具有作用域?我试图将此解决方案转换为“传统的、明确的实现”是错误的吗?如果不是,什么是“显式对应物”

    您的lambda被编译成私有合成方法,如下所示:

    private static void lambda$1(One one) { one.bar(); }
    
    在运行时,lambda表达式将在您第一次执行此代码时转换为运行时表示形式。OpenJDK 8中当前的运行时表示是一个匿名类,它将捕获的变量作为构造函数参数存储到字段中,然后调用编译器生成的lambda body方法。生成了如下内容:

    class lambda12345678 implements F {
        private One arg1;
    
        lambda12345678(One arg1) {this.arg1 = arg1;}
    
        public void foo() {
            lambda$1(arg1);
        }
    }
    
    而调用站点在技术上被构造函数调用取代,因此

    F f = ()->{one.bar();};
    
    你实际上有

    F f = new lambda12345678(one);
    

    请注意,如果lambda不捕获上下文,它将以更高效的方式工作:只创建和重用一个对象。但在您的示例中,lambda行为取决于外部状态,每次创建新实例时。

    太好了!这是我能想到的唯一可能性,即为子类创建了一个“隐藏”实例变量。您有任何资源可以链接吗?@LuigiCortese,此行为未指定,将来可能会更改。因此,最好的信息源是JDK源代码。您可以看到构造函数是如何在运行时生成的。请参阅添加的PUTFIELD指令,这些指令实际上将参数保存到字段中。谢谢。最后一件事,我没有收到你答案末尾的“便条…”。请您进一步解释一下好吗?@LuigiCortese,如果lambda没有捕获变量(如
    IntBinaryOperator lambda=(a,b)->a+b
    ),则在第一次调用时会创建生成的类的实例,但在进一步调用此代码时会重用相同的实例。类似于它被存储到内部常量中,您的代码被替换为
    IntBinaryOperator lambda=constant\u lambda\u对象,而不是像在您的案例中那样使用
    IntBinaryOperator lambda=new lambda12345678()
    。您没有“对实例方法的引用…”,因为您没有任何方法引用。除此之外,还不清楚为什么您难以想象相应的“传统的显式实现”,因为内部类也可以访问其周围上下文的局部变量。@Holger您是对的,我刚刚更正了语法。我的困惑可能比我想象的更广泛:我知道局部类的可见性,但我完全没有理解“自动生成构造函数”(如前所述)的概念,而且,当涉及lambda表达式时,幕后发生了一些稍微不同的事情(例如,不需要局部变量来[有效地]lambda表达式和内部类在访问局部变量方面没有区别。两者都要求局部变量为
    final
    或有效的final。这与方法引用不同。form expression::name的方法引用将立即计算表达式并捕获结果。因此,表达式也可能是对可变变量的引用,因为直接计算将产生其当前值并捕获该值。在您的示例中,
    one
    实际上是最终的,但即使是
    F=newone()::bar是有效的,因为它捕获了新对象…@Holger“expression::name将立即计算表达式并捕获结果”是什么意思?正如前面所说,当您编写
    F=newone()::bar
    ,然后在创建类型为
    F
    的对象时,将立即计算
    new One()
    ,并将结果(一个
    One
    的实例)绑定到
    bar
    的方法引用。
    f.foo()
    的后续调用将始终使用创建
    f
    时捕获的
    One
    对象。这与
    F=()->newone().bar()完全不同
    ,每次调用
    f.foo()
    时都会创建一个新的
    实例。