给我感觉,这是一门最小完备的语言,功能实用,无以复减,
语法上主要印象有 6 方面,
1.使用 val、var 来动态声明,编译时则会编译成静态类型,
2.数字字面量可以写成 val num = 1_000_000.99_99 方便人类查阅,
字符串字面量可以写成
1 |
|
无需 + 拼接,无需 \n 换行,
1.控制流可以直接作为表达式来返回结果,例如
1 |
|
2.函数也可直接以表达式方式书写,例如
1 |
|
3.区间可以更直观简便,例如循环的步进可以这样写
1 |
|
4.in
、 ..
、 ..<
,都是中缀表达式,开发者也可通过 infix 修饰的函数自定义中缀,
1 |
|
kotlin 允许函数和变量可以不是类成员,直接定义在文件中,作为顶层函数/变量供全局访问,
如此一来,蝴蝶效应,引发诸多连锁改善,
1.由于函数可以不是类成员,kotlin 的语法索性统一为,不得在参数中为类成员赋值,如此便为赋值运算符用作 “参数默认值” 预留了空间,
反观 java,由于方法总是类成员,延伸出可在方法实参中为类成员赋值的写法,而这种鸡肋用法对后来 “参数默认值” 语法的出现造成阻碍,
1 |
|
2.由于函数和变量可以和类一样直接在文件中声明(作为 “头等公民”),故此延伸出拓展、闭包、内联、委托等,
这拓展即是以顶层函数/变量的形式,对类成员进行拓展。在拓展函数中,可以像在类中一样,通过 this 访问到当前类对象,
1 |
|
这闭包(也即 lambda,个人习惯称之为闭包),可直接用在函数参数中,实现回调,
并且如果闭包是函数唯一的参数,那么调用该函数时,可以省略 ( ),直接写闭包 { },
1 |
|
3.由于闭包在编译时是生成一个匿名对象,如果闭包被用在循环中,会不断生成匿名对象,影响性能,故此参数有闭包的函数,一般会通过 inline 内联来修饰,
如此编译时,不会为闭包生成匿名对象和方法来与目标连接,而是直接将闭包中的内容粘贴到目标位置,即便是循环,性能也和代码是直接写在循环中并无二致,
1 |
|
4.既然有拓展,那么除了拓展函数,也可定义拓展属性,
拓展属性除了简化 getter setter 的调用,某种程度上也简化了委托,也即可在 getter setter 中植入想要的委托代码,如此在访问该属性时,能顺带执行这些代码,
1 |
|
5.那么拓展、闭包、内联一组合,便能形成一些实用的 “作用域函数”,例如 apply 函数,
可见不仅是 T.apply 用到拓展,T.( ) 也用到,这使目标对象例如 Paint( ) 在使用 apply 函数时,得以在闭包 { } 中通过隐式 this 来访问 paint 的属性,例如 color、style 等,
1 |
|
1.kotlin 通过 ? 操作符彻底解决 java 的 null 安全问题(以下简称 NPE),开发者无需再写 if ( null != x )
,也无需使用特定框架来规避特定领域的 NPE,
2.例如从前在 java 下通过 DataBinding ObservableField 的间接通知来规避 “横竖屏布局的 View NPE”,而这涉及 BindingAdapter、xml 属性绑定等一系列前置工作,
如今 kotlin 下可以直接通过使用 ViewBinding,书写便捷,性能还更高些,
1 |
|
3.除了 ?.
跳过空对象调用,还有 ?:
操作符来提供非空默认值,
例如 val b = a ?:
“xxx”,如 a 为 null,则表达式返回 ?:
后面值,
4.对应的,kotlin 下类型分为 “可空” 和 “不可空” 两种,可空类型都是 Any? 子类,不可空类型都是 Any 子类。多数情况下会是创建可空类型,然后使用 ? 操作符,
1.在 java 中,若想避免主线程被阻塞,通常会切到一个子线程,让子线程去阻塞,
不过想要再切回来,只能通过回调,如此在某些场景下可能引发多层嵌套(回调地狱),
并且如果希望多个任务并发(concurrent)或并行(parallel)执行,且执行后在同一时间汇合、继续下一步,java 线程不是那么好操作,需要手动安排亲和度等细节,
故此 kotlin 下可以使用协程,
2.在协程中,原本需要回调的异步方式,可以同步方式书写,
以下我们在主线程中,通过 MainScope.launch 启动一个协程,协程中共包含 4 个步骤,分别是打印开始信息、执行任务 1、任务2、打印结束信息,
按照以往 java 线程的方式,应该会先打印开始信息、结束信息,然后打印任务1、任务2,
而协程不一样,当协程中所有子任务都切到子线程开始运行时,整个协程被挂起,也即打印完开始信息、任务 1 和任务 2 都切到子线程时,整个协程立刻被挂起,避免主线程被阻塞,
期间在子线程中分别打印了任务 1、任务 2,
待到任务 1 任务 2 都完成,这个协程又被恢复,在步骤 4 中,拿着任务 1、任务 2 各自返回的异步结果,来打印结束信息,
整个运行过程就如同书写的那样,符合人类预期,
1 |
|
3.对于 IO 密集型任务,可在子任务中安排 Dispatcher.IO 调度器,其底层是使用 “动态线程池”,使尽可能多的处理 IO 请求(阻塞的线程不再主动抢时间片,无需担心占用计算型线程的时间片),
而对于计算密集型任务,可在子任务中安排 Dispatcher.Default 调度器,其底层是使用 “并行线程池”,根据 CPU 核心数,安排固定数目的线程到不同核心,最大限度减少任务完成时间,
如果只能用一张图来形容 “对 kotlin 的感觉”,笔者认为 flow 可担此任,
1 |
|
如上,伪代码中涵盖了 flow、sealed class、协程调度器等元素,示意 “在子线程中处理任务,并在过程中发射 ‘密封类结果’ 到主线程观察者” 的响应式开发模式,
软件形形色色,无非 “数据来,数据去”,语言则是实现这一切的工具,
从这角度来看,响应式开发、单向数据流,浑然天成、理所当然 —— 软件开发本就如此应当,
kotlin 是 java IDE 厂商 jetbrain 打磨 12 年之作,通过语法糖实现了诸多 “实用、且原本在 java 中需要手写乃至成本高昂” 的特性,所以 kotlin 可以说是对 java 及其设计模式的最佳实践,
有人说 kotlin 是 java pro max,笔者认为 kotlin 是语法最小完备的现代化语言,功能实用,无以复减,
目前唯一觉得不适的是 jvm 内存占用,在 jetbrain IDE 眼里,内存跟不要钱似的;在苹果眼里,内存比黄金还贵,而运行在 jvm 上的 kotlin,也是 400MB 起步,在 8GB 丐版 mac 下也就能跑 10 来个,
期待 jetbrain 对 kotlin/native 的持续发力,使让 macOS 等桌面操作系统也能用上内存只占 60-70MB 的软件,