为什么方法引用可以使用非最终变量?

我对内部类和lambda表达式有些困惑,我试图问一个关于这个问题的问题,但后来出现了另一个问题,发布另一个问题可能比评论前一个问题更好。

直截了当:我知道(谢谢乔恩)这样的东西不会编译

public class Main {
    public static void main(String[] args) {
        One one = new One();

        F f = new F(){      //1
            public void foo(){one.bar();}   //compilation error
        };

        one = new One();
    }
}

class One { void bar() {} }
interface F { void foo(); }

由于Java管理闭包的方式,因为不是[有效]最终的等等。one

但是,为什么这是允许的呢?

public class Main {
    public static void main(String[] args) {
        One one = new One();

        F f = one::bar; //2

        one = new One();
    }
}

class One { void bar() {} }
interface F { void foo(); }

不等同于 ?在第二种情况下,我不是面临着“使用过时的变量”的风险吗?//2//1

我的意思是,在后一种情况下,执行后仍然有一个过时的副本(即引用旧对象)。这难道不是我们试图避免的那种模棱两可吗?one = new One();fone


答案 1

方法引用不是 lambda 表达式,尽管它们可以以相同的方式使用。我认为这就是造成混乱的原因。以下是Java工作原理的简化,它不是它真正的工作方式,但它足够接近。

假设我们有一个 lambda 表达式:

Runnable f = () -> one.bar();

这相当于一个匿名类,它实现:Runnable

Runnable f = new Runnable() {
    public void run() {
       one.bar();
    }
}

此处,适用于匿名类(或方法局部类)的规则相同。这意味着需要有效地最终才能使其工作。one

另一方面,方法句柄:

Runnable f = one::bar;

更像是:

Runnable f = new MethodHandle(one, one.getClass().getMethod("bar"));

与存在:MethodHandle

public class MethodHandle implements Runnable {
    private final Object object;
    private final Method method;

    public MethodHandle(Object object, java.lang.reflect.Method method) {
        this.object = Object;
        this.method = method;
    }

    @Override
    public void run() {
        method.invoke(object);
    }
}

在这种情况下,分配给的对象是作为创建的方法句柄的一部分分配的,因此它本身不需要有效地最终工作。oneone


答案 2

您的第二个示例根本不是 lambda 表达式。这是一个方法参考。在这种特殊情况下,它从特定对象中选择一种方法,该对象当前由变量 引用。但是引用对象,而不是变量对象one

这与经典的Java案例相同:

One one = new One();
One two = one;
one = new One();

two.bar();

那么,如果改变呢? 引用以前的对象,并可以访问其方法。onetwoone

另一方面,您的第一个示例是一个匿名类,它是一个经典的Java结构,可以引用它周围的局部变量。代码引用实际的变量,而不是它所引用的对象。由于Jon在您提到的答案中提到的原因,这受到限制。请注意,Java 8 中的更改仅仅是变量必须有效地是最终的。也就是说,初始化后仍然无法更改它。编译器只是变得足够复杂,可以确定哪些情况不会混淆,即使没有显式使用修饰符。onefinal