当前位置: 首页 > 知识库问答 >
问题:

为什么java 8 lambdas允许访问非最终类变量?[重复]

秦承允
2023-03-14

我理解为什么编译器不接受以下内容:

class Foo {
    public Supplier<String> makeSupplier() {
        String str = "hello";
        Supplier<String> supp = () -> return str;

        // gives the expected compile error because
        // str is not effectively final
        // (str is a local variable, compile-time error
        //  as per JLS 15.27.2.)
        str = "world";

        return supp;
    }
}

令我困惑的是编译器接受以下内容,并且单元测试通过了:

class Bar {
    private String str = "hello";

    public void setStr(String str) {
        this.str = str;
    }

    public Supplier<String> makeSupplier() {
        Supplier<String> supp = () -> { return str; };
        return supp;
    }

    @Test 
    public void Unit_lambdaCapture() {    
        Supplier<String> supp = makeSupplier();
        Assert.assertEquals(supp.get(), "hello");
        setStr("foo");
        Assert.assertEquals(supp.get(), "foo");
    }
}

为什么上面的是有效的,工作正常?欢迎提供JLS相关章节的链接(第15.27.2节。只谈局部变量)。

共有2个答案

劳昊明
2023-03-14

它没有。它允许有效地访问最终类变量。

一个变量或参数的值在初始化后从未改变,它实际上是最终的。

资料来源:http://docs.oracle.com/javase/tutorial/java/javaOO/localclasses.html

苏乐
2023-03-14

我们都同意第一个示例不起作用,因为局部变量或参数必须是最终的或有效的最终的,才能在 lambda 表达式体内使用。

但是您的第二个示例不涉及局部变量或参数,因为 str 是一个实例字段。Lambda 表达式可以像访问实例方法一样访问实例字段:

15.27.2.λ体

lambda主体要么是单个表达式,要么是一个块(第14.2节)。与方法主体一样,lambda主体描述了在调用发生时将执行的代码。

实际上,Java 编译器从 lambda 表达式中创建了一个私有方法 lambda$0,该方法仅访问实例字段 str

private java.lang.String lambda$0() {
    0 aload_0;                /* this */
    1 getfield 14;            /* .str */
    4 areturn;
}

另一种观点:您还可以使用普通的旧匿名内部类实现供应商

public Supplier<String> makeSupplier() {
    return new Supplier<String>() {
        public String get() { return str; }
    };
}

从内部类访问实例字段是非常常见的,而不是Java 8的特长。

 类似资料:
  • 问题内容: 我了解为什么编译器不接受以下内容: 让我感到困惑的是,编译器接受以下内容,并且单元测试通过了: 为什么上述方法有效并且可以正常工作?欢迎使用指向JLS相关部分的指针(15.27.2节仅讨论局部变量)。 问题答案: 我们都同意第一个例子不能用,因为局部变量或参数必须是最终的或有效的最终才能在lambda表达式主体中使用。 但是您的第二个示例不涉及局部变量或参数,而是实例字段。Lambda

  • 我已经阅读了许多关于堆栈溢出的文章,试图找出为什么下面的代码1不起作用,但代码2起作用。我发现在6版和7版的不同版本中,行为或编译器存在不一致性https://stackoverflow.com/questions/13864464/use-of-uninitialized-final-field-with-without-this-qualifier.这更多地涉及访问默认的最终变量,无论是否有“

  • 问题内容: 在这里只能是最终的。为什么?如何在不保留为私有成员的情况下重新分配方法? 单击该如何返回?我的意思是, 问题答案: 如注释中所述,其中一些在Java 8中变得无关紧要,在Java 8中final可以隐式使用。但是,只能在匿名内部类或lambda表达式中使用有效的最终变量。 这基本上是由于Java管理闭包的方式。 创建匿名内部类的实例时,该类中使用的任何变量都将通过自动生成的构造函数复制

  • 我遇到了下面的Java代码,起初看起来不错,但从未编译过: 下面是IDE :变量USER_ID可能已分配的错误消息。 将值赋值给静态最终变量有问题吗?

  • 问题内容: 这个问题已经在这里有了答案 : 为什么在匿名类中只能访问最终变量? (15个答案) 为什么实例变量“忽略Lambda表达式中使用的变量必须是最终变量或实际上是最终变量”警告[重复] (2个答案) Lambdas:局部变量不需要最终变量,实例变量不需要 (10个答案) 2年前关闭。 当我编写此代码时,我收到一个编译时错误,该错误是: “ lambda中的变量必须是final或有效的fin

  • 问题内容: 我读了这个问题不可变对象,并留下了关于不可变对象,并最终场一个问题: 为什么我们需要不可变类中的实例变量为最终变量? 例如,考虑以下不可变的类: 如果在上面的代码中没有set方法,而实例变量仅在构造函数中设置,为什么要求将实例变量声明为final? 问题答案: 有没有 要求 这样做的变量。但是,当您确实明确打算永远不更改变量时,通常这样做是一种好习惯,因为这不仅可以使变量避免错别字或其