在lambda中使用类字段

时间:2017-07-12 10:50:45

标签: java lambda java-8

我不明白这种行为。

这段代码符合:

public class A {

    private final String s;

    private Function<String, String> f = e -> s; // Variable 's' might not have been initialized

    public A(String s) {
        this.s = s;
    }
}

但如果我使final成为最终版,那么我会收到编译错误:

final

为什么?如果是另一种方式,我明白了,但是当我声明一个字段[assembly: log4net.Config.XmlConfigurator(Watch = true)] (这迫使我在构造函数中初始化它的值)时,编译器是怎么抱怨的,当它是的时候就可以了。不是::-webkit-scrollbar { width: 50px; height: 18px; &-track { border: 0px; border-radius: 0px; background-color: transparent !important; } &-thumb { background-color: rgba(213, 213, 213, 0.4) !important; border: 0px; border-radius: 0px; } } // for new opened files .vertical-scrollbar { width: 50px !important; }

3 个答案:

答案 0 :(得分:12)

它与lambda无关,这个例子有同样的错误:

public class Test {
    private final String a;
    private String b = a; // // Variable 'a' might not have been initialized

    public Test(String a) {
        this.a = a;
    }
}

这是因为声明位置的初始化是在构造函数之前执行的。因此,在声明b的地方,a仍未初始化。

使用此示例时很清楚:

public class Test {
    private String a = "init";
    private String b = a;

    public Test(String a) {
        this.a = a;
    }

    public static void main(String[] args) {
        System.out.println(new Test("constructor").b);
    }
}

当您运行它时,会打印"init"(最初分配字段a的值)而不是"constructor",因为b的初始化发生了运行构造函数之前。

你的例子中的lambda使它更复杂,因为我们可以预期,由于对a的访问被推迟,编译器就可以了,但显然编译器只遵循一般规则“在初始化之前不要访问变量”。

您可以使用访问者方法绕过它:

public class Test {
    private final String a;
    private String b = getA(); // allowed now, but not very useful
    private Function<String, String> f = e -> getA(); // allowed now and evaluated at the time of execution of the function

    public Test(String a) {
        this.a = a;
    }

    public static void main(String[] args) {
        System.out.println(new Test("constructor").b); // prints "null"
        System.out.println(new Test("constructor").f.apply("")); // prints "constructor"
    }

    public String getA() {
        return a;
    }
}

答案 1 :(得分:5)

非最终成员变量将始终被初始化(因为它具有默认值 - null变量的情况下为String,因此不可能未初始化它。

另一方面,最终变量只能初始化一次,所以我假设它没有初始化为默认值。

我发现最接近的相关内容是JLS 4.12.4.

  

4.12.4。最终变量

     

变量可以声明为final。最终变量只能分配一次。如果指定了最终变量,则为编译时错误,除非在分配之前明确未分配

我假设我们可以理解最后一句话意味着最终变量没有被赋予默认值,否则你将在this.s = s;得到编译时错误。

更好的JLS参考(感谢Holger)是JLS 16

  

第16章。明确分配

     

对于本地变量或空白最终字段x的每次访问,必须在访问之前明确分配x,否则发生编译时错误

这个要求背后的理性是(在你的例子中)lambda表达式可以在初始化s之前调用:

public A(String s) {
    String v = f.apply("x"); // this.s is not initialized at this point
                             // so it can't be accessed
    this.s = s;
}

请注意,在初始化最终变量之后,您可以在构造函数中初始化lambda表达式(我将参数的名称更改为与成员变量不同,以便lambda表达式不会获取该局部变量): / p>

public A(String so) {
    // f = e -> s; // Error: The blank final field s may not have been initialized
    this.s = so;
    f = e -> s; // works fine
}

答案 2 :(得分:0)

这也是可行的方法

 public class A {

   private final String s;
   private Function<String, String> f;

   public A(String s) {
    this.s = s;
    this.f = e -> s;
   }
}