Java和Kotlin闭包的理解

 

1.闭包的概念

  • 闭包就是能够读取其他函数内部变量的函数。例如在javascript中,只有函数内部的子函数才能读取局部变量,所以闭包可以理解成“定义在一个函数内部的函数“。在本质上,闭包是将函数内部和函数外部连接起来的桥梁。--百度百科

第一句总结的很简洁了:闭包就是能够读取其他函数内部变量的函数。(这句话背会,要考!)

只说概念不好理解,下面上例子:

1.1 Java中闭包的实现(伪闭包?)

public class ClosureTest {
    interface Play {
        void playGame();
    }

    final private String hour = "1";//被内部类访问的变量,必须为 final

    public void test1(final String h) {
        //成员内部类
        Play p = new Play() {
            @Override
            public void playGame() {
                System.out.println("playGame" + h + "小时");//在这里访问了传入的外部变量h
            }
        };
        p.playGame();
    }

    //测试写法
    public void main() {
        test1(hour);//在这里将外部变量hour传了进去
    }

}

从上面代码可以看到,内部类Play访问了ClosureTest 类的成员变量hour。这就是闭包在java中的一种简单实现。(至于为什么要用final字段,下文有详细说明)

 

1.1.1 java中闭包的作用

OK,实现看完了,那这么做有什么用呢:

  • 核心作用:将变量生命周期拉长了。

解释:在上例中hour字段本身是ClosureTest的内部变量,生命周期是与ClosureTest绑定的。 但是内部类Play引用了该字段,因此hour的生命周期被拉长到Play执行结束。

  • 因为是用内部类实现的,所以当然也就拥有了内部类的功用。

内部类最重要的意义之一便是:间接让java拥有多继承的能力。至于内部类的具体功用,不是本篇重点,便不再赘述。

 

1.1.2 为什么要用final

上文还留有一个疑问:为什么一定要用final字段修饰变量呢?

这个就涉及到java老生常谈的一个问题了:多线程问题。

想象一下:当线程1被销毁之后,线程1中的变量也随着销毁了。那么这时如果线程2引用了线程1的变量是不是就出问题了呢??这就是java中被内部类引用的外部变量需要用final修饰的原因。

那么,问题又来了:为什么用final修饰变量就可以避免这个问题呢?

(以下内容部分引自 https://www.jianshu.com/p/f55b11a4cec2

这里只放结论:在java中,当主方法结束时,局部变量会被cleaned up 而内部类可能还在运行。当局部变量声明为final时,当使用已被cleaned up的局部变量时会把局部变量替换成常量。

解释:在java中,如果局部变量被声明称final。在使用时,如果该变量被销毁。在使用该变量的地方会复制一份该变量的值存储起来并使用。又因为是final,所以该值不会变,也就等同于使用了被销毁的变量。但是,这样的话其实在变量被内部类使用的那一刻他的值就是固定了的,无法修改且不受外部变量变化的影响。 这也是java闭包被称为伪闭包的原因。

 

1.2 Kotlin中的闭包

想要理解kotlin中闭包的实现,首先要懂kotlin中的一个概念:在Kotlin中,函数是“一等公民”。

对比一下java和kotlin更好理解:

java代码:

public class TestJava{

    private void test(){
        private void test(){//错误,因为Java中不支持函数包含函数

        }
    }
}

在java中是不支持这种写法的,因为函数是“二等公民”。

下面再看下kotlin代码:

fun test1(){
    fun test2(){//正确,因为Kotlin中函数是一等公民,函数可以嵌套函数,甚至函数可以作为返回值或者参数进行传递
        
    }
}

是不是大概明白了?如果还是不太懂的小伙伴可以先了解下函数式编程。推荐这篇文章:http://www.ruanyifeng.com/blog/2012/04/functional_programming.html 这位大神写的非常简单易懂,在我学习函数式编程时这篇文章给了我极大的帮助。再此表示感谢!

大家可能发现了,既然kotlin可以函数嵌套函数。那么就完全不用java那一套内部类+接口去实现闭包了!!!

那么kotlin中的闭包实现完全可以这么写:

    fun test(): () -> Unit {
        var a = 0
        return fun() {
            a++
            println(a)
        }
    }

    fun main() {
        val t = test()
        println(t())
        println(t())
        println(t())
    }

是不是发现了新世界的大门,内部函数很轻松地调用了外部变量a

这只是一个最简单的闭包实现。按照这种思想,其他的实现例如:函数、条件语句、Lambda表达式等等都可以理解为闭包,这里不再赘述。不过万变不离其宗,只要记得一句话:闭包就是能够读取其他函数内部变量的函数

 

1.3 总结和思考

看到这里,大家是不是对闭包有了简单的理解了呢?这里再次强调一下概念:闭包就是能够读取其他函数内部变量的函数。

关于闭包的概念只需要记住这一句话,千万别搞混了。因为很多语言中都有闭包这个词,但是说法却又不尽相同。盲目去看的话会导致大家越看越混乱。

强行背概念记忆总是不够深刻。不知道有没有小伙伴有这种疑惑:闭包这个词为什么是 “能够读取其他函数内部变量的函数”。虽然我的英语也不咋地,但是我也知道Closure这个单词有关闭、终止的意思啊~~ 可是再看上面那句话,哪里关闭了?哪里终止了?好像和“闭”这个词完全没有半毛钱关系啊??如果你也有以上疑惑,请继续往下看。

 

2.“闭包”一词的由来

这一章的灵感完全来自于这边文章:https://www.jianshu.com/p/c22db2a91989 作者详细讲述了闭包的概念和由来,想要深入了解的同学可以直接看这篇文章。下面我引用并简单解释下“闭包”一词的由来。

lambda表达式的闭包是定义在外部上下文(环境)中特定的符号集,它们给这个表达式中的自由符号赋值。它将一个开放的、仍然包含一些未定义的符号lambda表达式变为一个关闭的 lambda表达式,使这个lambda表达式不再具有任何自由符号。

OK,关键字关闭出来了。具体细节大家可以通读一下上面那篇文章。

 

哦了,本篇到此为止。有疑问或者建议欢迎大家提出~~~~

 

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值