1 闭包概述

闭包,Clousure。在Java中表现为Lambda表达式,在Koltin中表现为函数类型变量(或者也叫做Lambda表达式)。

其定义:java为什么匿名内部类的参数引用时final? - 胖胖的回答

  1. 一个依赖于自由变量的函数

  2. 处在含有这些自由变量的一个外围环境

  3. 这个函数能够访问外围环境里的自由变量

2 Kotlin和Java的闭包对比

在java8中,java中的闭包类型依然不能直接地修改外部环境的变量的引用,因此就有了众所周知的:匿名内部类的实例对外部环境的变量的访问,都需要让外部环境的变量加上final关键字。

在Kotin中,这一点被改进了,Kotlin中的闭包是功能健全的闭包。

3 代码

下面用简单的代码来对比java和kotlin的闭包类型变量在访问外部环境变量时的差异。

先定义了一个类型:鸟

  1. class Bird(private val wordToSing: String) {
  2. fun sing() {
  3. log("I sing $wordToSing")
  4. }
  5. }

3.1 Kotlin

  1. fun main() {
  2. var bird: Bird? = Bird("i 'm a testing bird.")
  3. val fun1 = {
  4. bird?.sing()
  5. bird = null
  6. }
  7. val fun2 = {
  8. if (bird != null) {
  9. log("bird is alive")
  10. } else {
  11. log("bird is destroyed")
  12. }
  13. }
  14. fun2()
  15. fun1()
  16. fun2()
  17. }

在上述代码中,有两个闭包:fun1fun2,他们的外部环境是main()函数,外部环境中的变量是var bird变量,那么两个闭包中都自由地对这个外部变量进行了读写。跑上述代码,打印:

  1. bird is alive
  2. I sing i 'm a testing bird.
  3. bird is destroyed

3.2 Java

  1. public class Test {
  2. public static void main(String[] args) {
  3. Bird bird = new Bird("i 'm a testing bird");
  4. Runnable fun1 = () -> {
  5. bird.sing();
  6. bird = null;
  7. };
  8. Runnable fun2 = () -> {
  9. if (bird != null) {
  10. UtilsKt.log("bird is alive");
  11. } else {
  12. UtilsKt.log("bird is destroyed");
  13. }
  14. };
  15. fun2.run();
  16. fun1.run();
  17. fun2.run();
  18. }
  19. }

用了和Kotlin完全一样的逻辑,编译期就报错了,IDEA提示我variable used in a lambda expression should be final or effectively final

但是,给bird变量加上final之后,在fun1函数里面对bird=null的赋值语句就出错了,因为final引用是没办法二次赋值的。

所以Java里面的闭包,对外部变量只能读,不能写(但是可以修改这个外部变量的属性)。

4 总结

Kotlin的闭包设计更强大。