你好,我是朱涛。
博客两年没怎么更新了,过去的两年里发生了很多事情:
- 第一,我成为了一名父亲;
- 第二,郭霖大佬鼓励了我,让我去申请了GDE,很荣幸,我通过了Google的筛选和面试,成为了Kotlin、Android的GDE。(希望疫情赶紧过去,我还欠大佬一顿饭。)
- 第三,我从上家公司离职,花了半年写了一个技术专栏《Kotlin编程第一课》,体验了一次自由职业者的感觉。
在撰写技术专栏的过程中,我成长了很多,既有技术方面的成长,也有技术之外的成长。在过去的半年里,我做的最多的事情就是:深度思考。我要想尽一切办法,将自己脑子里的知识掏出来,然后,以最简单、最直观的方式呈现给我的读者。比如说,为了解释清楚Kotlin协程的“挂起和恢复”,我掉了很多头发才找到下面这个灵感:
说实话,我写专栏,根本不是为了钱,单纯就是因为喜欢做这样的事情。这半年里,我写作的态度比从前的工作还拼命;半年里挣的钱,还不如我一个月工资。但我乐在其中啊,哈哈!
对我来说,读者对课程的赞扬,就是我快乐的源泉。其实,很多互联网的大佬写博客、写书,也是同理。
关于「沉思录」
其实,在这半年里,我还积累了许多创作灵感,这些灵感,大都非常的琐碎,并不足以成为专栏系统课程的一部分。但这些灵感如果不写出来,就太可惜了。所以,我决定将其取名为:「沉思录」,以博客的形式发出来。
「沉思录」,大约分为三个板块:
- Kotlin沉思录,记录我在Kotlin领域的思考,包括Kotlin JVM,协程,也包括KMM;
- Android沉思录,记录我在Android、Jetpack领域的思考,两年前我写的《Kotlin Jetpack实战》还是太浅了;
- Compose沉思录,自然就是Jetpack Compose领域的思考,它的编程理念,实现原理都非常有趣。
今天,我们主要看看Kotlin沉思录。
Kotlin 沉思录
初学Kotlin的时候,我更多的只是看到了Kotlin的「美」,但经过5年多的沉淀,我也渐渐发现了Kotlin「丑」的一面。
世界上不存在完美的语言,Kotlin也不例外。
其实,一直以来,总有读者问我这样问题:
“Kotlin的最佳实践是什么?”
对于这样的问题,我的回答总是非常的谨慎,说实话,我不敢仅凭我个人的经验就妄下定论。然而,最近,问我这个问题的人越来越多了,有专栏的读者,也有公司的同事。
接下来,我们就来聊聊具体的思路吧,关于Kotlin的「丑」与「美」。
「丑」与「美」
人们常说,穿衣服,要尽量遮住自己「丑」的部位,尽量凸显自己「美」的部分。我的思路也是类似的,简单来说,就是:扬长避短。
Kotlin的「弱点」
考虑到我GDE的身份,不能大肆宣扬Kotlin的缺点。因此,关于Kotlin「丑」的部分,我会以「弱点」、「坑」为标题。毕竟「丑」与「美」都是非常主观的一种判断,「弱点」则是一种更客观的描述。(没错,我就是比较怂。)
以下是部分主题:
- 02 | 枚举类的弱点在哪?
- 03 | Data Class的弱点在哪?
- 06 | Lambda的弱点在哪?
- 09 | 扩展函数的弱点在哪?
- 15 | Channel的弱点在哪?
- 16 | Flow的弱点在哪?
Kotlin之美
聊完Kotlin的「丑」之后,我们就知道Kotlin的「弱点」还有「坑」在哪了,在平时使用的时候,我们就可以很好的避开它的弱点了。
接下来,我们就可以来看看Kotlin的「美」了,欲扬先抑嘛。
以下是部分主题:
- 17 | 如何理解Kotlin语法之美?
- 18 | 如何理解sealed之美?
- 21 | 如何理解Delegation之美?
- 23 | 如何理解协程之美?
- 26 | 如何理解Flow之美?
- 30 | 如何理解inline之美?
**Kotlin的「最佳实践」到底是什么?**看完上面的这些系列以后,我相信每个读者都会有一个自己的答案吧!
Kotlin其实是一个非常宽泛的话题,除了语法特性层面的「丑」与「美」,其实还有许多能聊的东西,比如:编程范式、DSL设计、数据结构与算法、设计模式,KMM、Compose Multiplatform。这些内容,我都会尝试在这个系列的博客里去做一些涉猎。
关于深度
沉思录这个系列不是面向0基础读者的,这里主要会记录我平时的一些灵感和思考,它更像是不成体系的Android、Kotlin随笔。如果你是有一定经验的开发者,应该能让你有所启发。(不敢保证哈。)
如果你对Kotlin没有一个全面的认识,那我建议你先去看看Kotlin官方文档。如果你觉得官方文档枯燥乏味,也可以去看看我公众号里的历史文章,我自认为讲的还不错。
OK,闲聊结束,我们进入正题。
一个让人「又爱又恨」的特性
我们都知道,Kotlin会为成员属性自动生成getter、setter。不论是使用var、还是使用val,Kotlin编译器都会自动帮我们处理getter、setter的问题。
1 | kotlin复制代码// 代码段1 |
如果将上面的Kotlin代码反编译成Java的话,它大致会长这样:
1 | java复制代码// 代码段2 |
在我初学Kotlin的时候,我曾对着这个案例感叹:Kotlin 666!一行顶十行啊!用Kotlin开发,果然能大大提升效率啊!
说实话,现在回过头来看,当时的我就跟个“脑残粉”一样,大家不要学我。
好了,言归正传,有一说一,Kotlin自动生成getter、setter,它这样的行为,对比Java确实是一大进步,因为它可以避免外部的类直接访问Kotlin类当中的字段(Field)。毕竟,我们都知道,类似下面这样的Java代码是非常不合理的。
1 | java复制代码// 代码段3 |
上面的代码,既不符合开放封闭的原则,也难以维护。对比之下,Kotlin生成的代码段2的代码,则要顺眼很多。然而,Kotlin这样的策略其实也有它丑陋的一面。让我们来看看下面的例子。
Getter、Setter的第一个弱点
Kotlin的Getter、Setter主要有两个弱点,我们先来看它的第一个弱点,请看下面的代码。
1 | kotlin复制代码// 代码段4 |
请问,这段代码的问题在哪?抛开当前文章的语境,如果你是在面试当中遇到这样一个问题,你会给出怎样的答案?
你可以停下来思考一下,心里有了答案以后再继续往后看。
Kotlin最大的问题就在于,大部分的开发者很难意识到Kotlin编译器背后自动生成的那些getter、setter方法,从而导致这个特性被滥用。
让我们将上面的代码反编译来看看。
1 | kotlin复制代码// 代码段5 |
可以看到,注释1、2、3对应的这三个自动生成的getter、setter方法,它们根本就没被用到!这样的问题,如果是在一个非常小的规模,其实是无伤大雅的,但对于一个大的工程来说,当这样的问题积少成多,就会极大增加方法数,还有应用的包体积。这一点对Android应用尤为重要。
那么,以上的问题该怎么解决呢?答案其实也很简单:
1 | kotlin复制代码// 代码段6 |
我们将其反编译成Java看看:
1 | java复制代码// 代码段7 |
这样的 Java 代码看起来是不是干净了很多?这就对了!
反思
Kotlin编译器自动生成Getter、Setter的操作,它对比Java确实是存在优势的,因为它更符合「开放封闭」的原则。我们Kotlin开发者随手写出的var、val,它背后都会转换成Java当中的「最佳实践」,真的很方便。这个设计最精彩的地方在于:「简洁」,简单的var背后代表了:Field + Getter + Setter,这样的信息密度是Java所不能比拟的。不得不佩服Kotlin设计者的精妙构思。
然而,几乎所有的技术都是一种trade-off,Kotlin这样「方便」、「简洁」的设计,让它丢失了许多底层的信息。许多开发者只看到了var的方便,却很容易忽略它底层自动生成的Field + Getter + Setter。Kotlin官方其实也在想办法解决类似这样的问题,但这远远不够。
当我们写出有问题的代码时,IDE其实是会有警告的。可是,它只告诉了我们:desc可以变成private,并没有告诉我们具体的后果。当我们看到这样的提示时,我们脑子里很难将“private”与“Getter、Setter”建立对等的关系。
- 对于var属性来说,加上private修饰,就意味着
方法数减2
; - 对于val属性来说,加上private修饰,就意味着
方法数减1
;
其实,许多事物发展到一定程度,最后都会开始拼细节。考试是如此、商业是如此、编程也是如此。
好,考虑到篇幅限制,我们之后再聊Getter、Setter的第二个弱点吧!我们下期再见!
本文转载自: 掘金