虽然 Kotlin 推出了一段时间了,但是我一直持观望态度。之前一直觉得 Kotlin 只是在 Java 的基础上增加了一些新的特性,如果本质的性能问题没有提升,那么也没必要深入掌握这种语言。但最近因为开发的时候遇到空指针的判断问题,感觉 Java 的处理方式非常繁琐,所以希望诉诸于 Kotlin. 本次梳理的主要目标在于,
- 梳理 Kotlin 基本语法和语言设计思路,
- 了解 Kotlin 的语言特性并分析其实现方式。
1、基础特性
- 本质上是静态类型语言,编译期确定类型,但无需明确指定变量类型;(!:会不会导致编译过慢?)
- 对可空类型的支持,可以在编译期发现空指针;(Q:如何做到?以什么形式的提示给出?)
- 支持函数式编程,虽然 Java8 以后都支持了;
- 类文件的后缀名式
.kt
,编译之后还是生成 class 文件,只是编译器使用的是kotlinc
(对应于 javac),执行 class 的时候还是使用 java; - 可以使用转换器将 Java 转换成 kotlin;(Q:转换器在哪里?转换的效果怎么样?)
- 每行后面不需要加分号;
- Kotlin 标准库给 Java 库做了封装,我们可以简化原生 Java 库的调用;(Q:简化的规则?都有哪些库被简化了?)
- 数组就是类,没有专门用来声明数组的语法;
2、基本结构
2.1 类文件结构
在 Kotlin 中文件名称和文件的内容没有关系(在 Java 中文件名和类名相同),并且文件内部定义的是函数还是类都没关系。比如,下面是定义在目录 me/shouheng/demo1/FirstDemo.kt
中的类和函数,这里类和函数处于文件的同一层次。另外,一个文件中还可以定义多个类和多个函数,都没有问题。
package me.shouheng
class Person (age : Int, name : String) // 声明了一个类
class Person2 (val age : Int, val name : String) // 声明了一个类
class Person3 (val age : Int, val name : String) {
// 声明了一个类
var married : Boolean = false
// get() = married // 不允许会造成递归和栈溢出
val adult get() = age > 18
}
fun doSomething(person: Person) : Int {
// 在字符串中使用 “$+变量名” 的格式进行占位,相当于 "My name is" + persion + "!"
println("My name is $person!")
}
// “if else” 对应于 Java 中的 “? :” 三元运算符
fun max(a : Int, b : Int) : Int = if (a > b) a else b
fun min(a : Int, b : Int) = if (a < b) a else b
对于 Kotlin 来说,类和函数的真实包名是由文件中的 package 关键字指定的,与文件结构没有必然的关系。当然,我们建议按照 Java 的规则使其对应起来,因为这样维护起来更好、逻辑更清晰些。
上面是 Kotlin 中类和函数的定义的方式,总结下来区别有:
- 变量和返回值的类型被放在冒号后面;
- 函数的定义使用关键字
fun
,覆写的话就在函数名前面加上override fun
; - 在字符串中使用
$+变量名
的格式进行占位(这叫字符串模板),如果希望使用美元符号,前面加上反斜杠即可; - Kotlin 中没有 int 等关键字,只有 Int 等包装类;
- 可以直接像上面的 Person 那样定义类的时候同时声明构造方法;
- 可以像上面的 min() 和 max() 那样定义表达式函数,此时
: Int
可省略,因为类型可以推断; - 上面的 Person 和 Person2 有所不同,后者有两个局部变量
age
和name
而前者啥局部变量都没有; - Kotlin 中会将类的局部变量的访问权默认为 pulic 的,所以外部可以直接通过实例获取字段和赋值;
- 可以通过
get()
和set()
来重写 getter 和 setter 方法,但注意 Kotlin 中的类的字段兼有方法和字段两重含义,重写get()
的时候再调用该字段会不断递归调用,造成栈溢出!(所以,一般情况下,使用默认的 get() 和 set() 逻辑即可,这是符合规范的,如果想增加新的逻辑,可以增加一个新的方法,就像 adult 字段一样。)
2.2 程序基本结构
再以下面的程序为例,可以总结如下,
- 没有专门用来声明数组的,全部都是类,可以像下面这样声明数组,另外,可以按照
args[0]
这样获取数组元素; - 声明变量有
var
和val
两个关键字,系统可以自动推断类型,var
声明的变量可以二次赋值,而val
不行,后者相当于final
的; - 虽然
var
类型的变量可以二次赋值,但是两次赋值的类型必须相同; - 可以在声明变量的时候使用冒号指定变量的类型,像下面的 b 一样(大部分情况下可以省略,因为编译器可以自动推断);
- 初始化一个类的时候不需要
new
关键字(抛出异常的时候自然也一样);
fun main(args : Array<String>) {
test(args)
}
fun test(args : Array<String>) {
val a = Person(10, "Ming")
var b: Person
b = Person(11, "Xing")
doSomething(a)
doSomething(b)
}
2.3 枚举
声明枚举的方式如下,也可以给枚举添加一些方法,方式同 Java.
enum class City {
BEIGING, SHANGHAI, GUANGZHOU
}
enum class City2(level:Int) {
BEIGING(1), SHANGHAI(2), GUANGZHOU(3)
}
2.4 when
类似