如果你曾使用过匿名内部类,也许遇到过这样的情况:需要引用它所在方法里的变量。这时,需要将变量声明为final,如例2-5 所示。将变量声明为final,意味着不能为其重复赋值。同时也意味着在使用final 变量时,实际上是在使用赋给该变量的一个特定的值。 例2-5 匿名内部类中使用final 局部变量 final String name = getUserName(); button.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { System.out.println("hi " + name); } }); Java 8 虽然放松了这一限制,可以引用非final 变量,但是该变量在既成事实上必须是final。虽然无需将变量声明为final,但在Lambda 表达式中,也无法用作非终态变量。如果坚持用作非终态变量,编译器就会报错。 既成事实上的final 是指只能给该变量赋值一次。换句话说,Lambda 表达式引用的是值,而不是变量。在例2-6 中,name 就是一个既成事实上的final 变量。 例2-6 Lambda 表达式中引用既成事实上的final 变量 String name = getUserName(); button.addActionListener(event -> System.out.println("hi " + name)); final 就像代码中的线路噪声,省去之后代码更易读。当然,有些情况下,显式地使用final代码更易懂。是否使用这种既成事实上的final 变量,完全取决于个人喜好。 如果你试图给该变量多次赋值,然后在Lambda 表达式中引用它,编译器就会报错。比如,例2-7 无法通过编译,并显示出错信息:local variables referenced from a Lambdaexpression must be final or effectively final1。 例2-7 未使用既成事实上的final 变量,导致无法通过编译 String name = getUserName(); name = formatUserName(name); button.addActionListener(event -> System.out.println("hi " + name)); 这种行为也解释了为什么Lambda 表达式也被称为闭包。未赋值的变量与周边环境隔离起来,进而被绑定到一个特定的值。在众说纷纭的计算机编程语言圈子里,Java 是否拥有真正的闭包一直备受争议,因为在Java 中只能引用既成事实上的final 变量。名字虽异,功能相同,就好比把菠萝叫作凤梨,其实都是同一种水果。为了避免无意义的争论,全书将使用“Lambda 表达式”一词。无论名字如何,如前文所述,Lambda 表达式都是静态类型的。因此,接下来就分析一下Lambda 表达式本身的类型:函数接口。 注1: Lambda 表达式中引用的局部变量必须是final 或既成事实上的final 变量。——译者注