Java函数式编程(三)

前言

如果你对Lambda表达式感兴趣,想知道它怎么帮助你提升专业技能,那么,看到这篇文章就对了!我们假设大家还不知道Lambda表达式,以及Java的核心类库的变化,我们将从“零”开始介绍这些概念、类库和技术。

Standing on Shoulders of Giants

站在巨人的肩膀上

OR

如果你曾使用过匿名内部类,也许遇到过这样的情况:需要引用它所在方法里的变量。这时,需要将变量声明为final。这里我们给出一个Demo:

(匿名内部类中使用final局部变量)

final String name = getUserName();  button.addActionListener(new ActionListener(){     public void actionPerformed(ActionEvent event){        System.out.println("hi "+ name);     }  });                                                     

将变量声明为final,意味着不能为其重复赋值。同时也意味着在使用final变量时,实际上是在使用赋给该变量的一个特定的值。

从Java 8开始虽然放松了这一限制,可以引用非final变量,但是该变量在既成事实上必须是final。虽然无需将变量声明为final,但在Lambda表达式中,也无法用作终态变量。如果坚持用作终态变量,编译器就会报错。

既成事实上的final是指只能给该变量赋值一次。换句话说,Lambda表达式引用的是值,而不是变量。我们给出一个Demo:

(name就是一个既成事实上的final变量)

String name = getUserName();  button.addActionListener(event -> System.out.println("hi " + name));                                         

final就好像是代码中的线路噪声,省去之后代码更易读。当然了,在很多情况下,显式地使用final代码更易懂。是否使用完全取决于个人喜好。

如果你试图给该变量多次赋值,然后在Lambda表达式中引用它,编译器就会报错。我们给出一个Demo:

(未使用既成事实上的final变量,导致无法通过编译)

String name = getUserName();  name = formatUserName(name);  button.addActionListener(event -> System.out.println("hi " + name));                                         

编译器会显示出错信息:local variables referenced from a Lambda expression must be final or effectively final.

Lambda表达式中引用的局部变量必须是final或既成事实上的final变量。

实际上这种行为也解释了为什么Lambda表达式也被称为闭包。未赋值的变量与周边环境隔离起来,进而被绑定到一个特定的值。在圈子里,Java是否拥有真正的闭包一直备受争议,因为在Java中只能引用既成事实上的final变量。“名字虽异,功能相同”。但是,这里要说的是,无论名字如何,Lambda表达式都是静态类型的。因此我们在下一期将会分析Lambda表达式本身的类型:函数接口。

每周小结

• 在很多情况下,显式地使用final代码更易懂。是否使用完全取决于个人喜好。

每周名言

• 如今的编程是一场程序员和上帝的竞赛,程序员要开发出更大更好、傻瓜都会用到软件。而上帝在努力创造出更大更傻的傻瓜。目前为止,上帝是赢的。(Rick Cook)

每周问答

• 问 :我知道C++定义了一种名为析构函数(destructor)的特殊函数,可以在销毁对象时自动执行,finalize()与析构函数一样吗?

• 答 :Java没有析构函数。尽管finalize()方法与析构函数的功能相近,但两者不一样。例如,C++析构函数总是在对象不在作用域之前被调用,但是对于任何具体对象,都不知道如何调用finalize()。坦率的讲,因为Java使用了垃圾回收,所以没有必要使用析构函数了。