尝试Java,从入门到Kotlin(下)

上篇已提(tu)到(cao)Java中的各种坑。习惯了C#的各种特性和语法糖后,再转到Java感觉比较别扭。最后本着反正Java也不是很熟悉,干脆再折腾折腾其他语言的破罐子破摔的心态,逛了一圈JVM语言,最终决定转Kotlin

为何选择Kotlin

项目遭遇人员变动,包括我在内就剩两个人开发,转型成本低,代码质量容易控制。

JVM语言。号称与Java 100%兼容。实际使用的确能够与Java几乎无缝地相互调用,基本上可以无缝迁移,完美兼容Java生态。

OOP。目前OOP仍是主流,方便后续交接或者其它新加入的开发成员上手。

静态类型。在选择语言的时候也考虑过像Groovy,JRuby等的动态类型语言。然而俗话说得好,动态一时爽,重构火葬场。当项目变大的时候,静态类型支持的较为完善的语义分析能够帮助项目快速整理、重构代码。并且引入很多函数式特性后,静态类型语言的开发效率与爽感,不比动态类型语言低多少。

吸收了一些函数式特性。除了常见的lambda,map,filter,reduce之外,还吸收了ruby的一些如对象上下文切换、代码块语法糖等便捷的特性(但是也可能导致代码可读性下降)。

对JetBrain的信任。JetBrain在静态分析的成果上有目共睹。相信JetBrain设计的语言应该会比较有品位(然而严格得不近人情的null safety是有点让人纠结)。

最后,就是刚好看到Kotlin,确认了眼神……

Kotlin好用的特性 Lambda

牺牲了CE使得Lambda不像Java中那么多的约束。引入类似Ruby代码块的写法(默认it参数),让代码看起来比较好看,虽然我个人不是很喜欢这种默认约定,但是用起来真香。

面向表达式

不同于其他语言,Kotlin里的if else,try catch等都是表达式,我们可以直接这样子写代码:

val y = if (x % 2 == 0) "even" else "odd" val z = try { readFromFile() } catch (ex: IOException) { "" } DSL

Lambda是最后一个参数时,可以写在括号外面(学自ruby)。主要是用来让回调比较好看,和实现DSL。

val ls = listOf(1, 2, 3) ls.map { 2 * it } // returns [2, 4, 6]

Receiver。Kotlin不仅有纯函数类型,还可以通过Receiver声明类的方法类型。这个特性可以用来实现类的方法扩展、this切换的功能。
下面代码给Int扩展了个double方法:

val double = fun Int.() = 2 * this val x = 3.double() // x = 6

下面例子通过切换this实现了一个类似C#初始化对象的方法:

class Obj(init: Obj.() -> Unit) { var prop1: Int = 0 var prop2: String = "" init { init(this) } } val obj = Obj { prop1 = 1 prop2 = "abc" } 其他

很多好用的方法,像listOf,mapOf。to操作符等

……

Kotlin的坑 Kotlin没有final,但是有open。

Kotlin中Class默认都是不能继承的。需要继承的Class要在声明的地方加上open修饰。另外提一下有个插件叫all-open,专门用来让所有Kotlin的类变为可继承的……

注解的继承

Kotlin不支持可继承的注解。

纯的容器类型

List,Map不能修改其内部存储的元素。需要修改应该用MutableList和MutableMap。

Lombok

号称和Java 100%兼容,但是不能访问Lombok生成的方法!

因为Lombok的方法是编译期通过注解处理器(annotation processing)生成的,Kotlin编译时只调用了Javac,所以无法处理Lombok定义的方法。强制先编译Java代码,后编译Kotlin代码,可以解决这个问题,但是又会有新的问题:你不能在Java代码中调用Kotlin代码。所以如果你要混合使用Java和Kotlin的话,推荐所有数据类型都用Kotlin写。

val和var

var就是普通变量。val相当于const。平时尽量使用val,有益身心健康。

重头戏,null safety

Null safety是Kotlin宣传得最多的特性,但是我并没有放在“好用的特性”节中介绍,因为它的坑非常多,以至于我十分怀疑null safety的好处是否能抵消它带来的副作用。

所有类型默认都不包括null值,除非加个问号定义为Nullable类型。Nullable类型取值时,强制check null。如果调用Java代码,默认Java代码都是Nullable。不过从Java来的变量不做check null倒是不会报error,只报warning。如果运行时值为null的话,仍然会抛NullPointerException。Kotlin的null safety的特性其实只是一个编译器的特性,通过将null与其他类型区分开来,在类型检查的时候顺便检查了可能出现的NullPointerException,但是在运行时非Nullable的变量实际上也是可以放进去null值的(比如通过反射)。

由于非Nullable类型不被赋值为null值(废话),导致这些类型的变量可能会没有默认值!这是个严重的问题。如果是像Int,String这种比较像值的类型(其实也是引用类型)还好,可以有0,空字符串等默认值。而像自定义的类,这种类型的变量其实是个引用,如果不能默认为null的话,那么它的默认值的取值只能有这么几种方案:

类似C语言,未初始化的随机值:会产生更大更不确定硬隐蔽的问题。

定义一个“未初始化”的值:那么这个值和null有什么区别?又绕回来了。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zydgzx.html