点击咨询:郑州3+2学校 河南省技校 郑州电脑学校

首页 > 郑州北大青鸟学员作品 > Java学习 >

[Java] Java8中lambda表达式与闭包

时间:2016-01-18 17:35:26 作者:未知 点击: 0
在这里对JAVA8中lambda表达式与闭包给同学们进行简单的介绍。

    熟悉的Javascript或者Ruby的同学,可能对另一个名词:闭包更加熟悉。因为一般闭包的示例代码,长得跟lambda差不多,导致我也在以前很长一段时间对这两个概念傻傻分不清楚。其实呢,这两个概念是完全不同维度的东西。
   闭包是个什么东西呢?我觉得Ruby之父松本行弘在《代码的未来》一书中解释的最好:闭包就是把函数以及变量包起来,使得变量的生存周期延长。闭包跟面向对象是一棵树上的两条枝,实现的功能是等价的。
   这样说可能不够直观,我们还是用代码说话吧。其实Java在很早的版本就支持闭包了,只是因为应用场景太少,这个概念一直没得到推广。在Java6里,我们可以这样写:
public static Supplier testClosure(){
    final int i = 1;
    return new Supplier() {
        @Override
        public Integer get() {
            return i;
        }
    };
}
public interface Supplier {
    T get();
}
看出问题了么?这里i是函数testClosure的内部变量,但是最终返回里的匿名对象里,仍然返回了i。我们知道,函数的局部变量,其作用域仅限于函数内部,在函数结束时,就应该是不可见状态,而闭包则将i的生存周期延长了,并且使得变量可以被外部函数所引用。这就是闭包了。这里,其实我们的lambda表达式还没有出现呢!
 
而支持lambda表达式的语言,一般也会附带着支持闭包了,因为lambda总归在函数内部,与函数局部变量属于同一语句块,如果不让它引用局部变量,不会让人很别扭么?例如Python的lambda定义我觉得是最符合λ算子的形式的,我们可以这样定义lambda:
 
  #!/usr/bin/python
  y = 1
  f=lambda x: x + y 
  print f(2)
  y = 3
  print f(2)
  输出:
  3
  5
这里y其实是外部变量。
 
Java中闭包带来的问题
    在Java的经典著作《Effective Java》、《Java Concurrency in Practice》里,大神们都提到:匿名函数里的变量引用,也叫做变量引用泄露,会导致线程安全问题,因此在Java8之前,如果在匿名类内部引用函数局部变量,必须将其声明为final,即不可变对象。(Python和Javascript从一开始就是为单线程而生的语言,一般也不会考虑这样的问题,所以它的外部变量是可以任意修改的)。
 
在Java8里,有了一些改动,现在我们可以这样写lambda或者匿名类了:
 public static Supplier<Integer> testClosure() {
   int i = 1;
   return () -> {
     return i;
   };
  }
这里我们不用写final了!但是,Java大神们说的引用泄露怎么办呢?其实呢,本质没有变,只是Java8这里加了一个语法糖:在lambda表达式以及匿名类内部,如果引用某局部变量,则直接将其视为final。我们直接看一段代码吧:
 
  public static Supplier<Integer> testClosure() {
   int i = 1;
   i++;
   return () -> {
      return i; //这里会出现编译错误
   };
  }
 
    明白了么?其实这里我们仅仅是省去了变量的final定义,这里i会强制被理解成final类型。很搞笑的是编译错误出现在lambda表达式内部引用i的地方,而不是改变变量值的i++…这也是Java的lambda的一个被人诟病的地方。我只能说,强制闭包里变量必须为final,出于严谨性我还可以接受,但是这个语法糖有点酸酸的感觉,还不如强制写final呢…
 

(责任编辑:未知)
zuopin
6张图 弄懂软件开发
学员百分百就业满意

升学案例

数十家优质大学合作

更多了解

就业案例

高端就业,不做普通工

更多了解