01 | Kotlin基础语法:正式开启学习之旅
开发环境
可以直接使用 Kotlin 官方提供的 PlayGround 运行 Kotlin 代码片段。
推荐安装 IntelliJ IDEA(免费版即可),可以通过 File -> Project Structure -> SDKs -> + 选择第三方提供的 OpenJDK 。
推荐手动从 Oracle 官网下载 JDK 1.6、1.7、1.8、11 这几个版本,然后再安装、配置 Java 多版本环境。
变量
- 代码末尾的分号可以省略
- Kotlin 支持类型推导,大部分情况下,变量类型可以省略
- Kotlin 当中应尽可能避免使用 var(可变变量),尽可能多地使用 val(不可变变量),它相当于 Java 里面的 final 变量
var age: Int = 30;
var name: String = "bqt" // 代码末尾的分号可以省略
var job = "iter" // 变量类型可以省略
val city = "shenzhen" // 尽可能多地使用 val
基础类型
基础类型,包括我们常见的数字类型、布尔类型、字符类型,以及这些类型组成的数组。
一切皆对象
Java 的类型系统并不是完全面向对象的,因为它存在原始类型,而原始类型并不属于对象。
- 原始类型(Primitive Types):开销小、性能高,但它不是对象,无法很好地融入到面向对象的系统中
- 包装类型(Wrapper Type):开销大、性能差,但它是对象,有成员变量以及成员方法,可以很好地发挥面向对象的特性
而 Kotlin 则不一样,它从语言设计的层面上就规避了这个问题,类型系统是完全面向对象的,Kotlin 中是没有原始类型这个概念的。在 Kotlin 里,一切皆对象。
val i: Double = 1.toDouble() // 整型数字 1 是对象,所以我们可以调用它的成员方法
空安全
Kotlin 强制要求开发者在定义变量的时候,指定这个变量是否可能为 null。对于可能为 null 的变量,我们需要在声明的时候,在变量类型后面加一个问号 ?。
val i: Double = null // 编译报错
val j: Double? = null // 编译通过
并且,可能为空的变量无法直接赋值给不可为空的变量。如果有这样的需求,只要做个判断即可。
var i: Double = 1.0
var j: Double? = null
i = j // 编译报错
j = i // 编译通过
if (j != null) {
i = j // 编译通过
}
数字类型
Kotlin 和 Java 的数字类型基本是一致的,包括它们对数字字面量的定义方式。
val int = 1 // 整数默认会被推导为 Int 类型
val long = 1234567L // 需要使用 L 后缀
val double = 13.14 // 小数默认会被推导为 Double,我们也可以使用 D 后缀
val float = 13.14F // 需要使用 F 后缀
val hexadecimal = 0xAF // 使用 0x 来代表十六进制字面量
val binary = 0b01010101 // 使用 0b 来代表二进制字面量
Java 可以隐式转换数字类型,而 Kotlin 必须显式转换数字类型。Kotlin 这样设计的目的是,让我们代码的可读性更强,将来也更容易维护。
int i = 100;
long j = i; // 在 Java 中,可以直接把 int 类型赋值给 long 类型,编译器会自动做类型转换
val i = 100
val j: Long = i // 编译器报错!在 Kotlin 里,这样的隐式转换被抛弃了,必须显式转换
val j: Long = i.toLong() // 编译通过
布尔类型 Boolean
Kotlin 中布尔类型的变量只有两种值,分别是 true 和 false。布尔类型支持与或非逻辑操作。
val isTrue: Boolean = true
字符类型 Char
Char 用于代表单个的字符,比如'A'、'B'、'C',字符应该用单引号括起来。
val c: Char = 'A'
val i: Int = c // 编译器报错,不支持数字类型隐式转换
val i: Int = c.toInt() // 编译通过
字符串 String
和 Java 一样,Kotlin 中的字符串也是不可变的。Kotlin 中可以使用字符串模板。
val name = "Kotlin"
print("Hello $name!") // 直接在字符串中访问变量
print("Hello ${name}xxx!") // 有时需要使用花括号将变量括起来
Kotlin 中可以用三个双引号来表示一个原始字符串,用于存放复杂的多行文本,并且定义时是什么格式,最终打印时也是对应的格式。
val s = """
当我们的字符串有复杂的格式时
原始字符串非常的方便
因为它可以做到所见即所得。 """
print(s)
数组
- 在 Kotlin 当中,一般使用
arrayOf()来创建数组,同时,Kotlin 编译器也会根据传入的参数进行类型推导 - Java 中的数组和集合的操作是不一样的,而 Kotlin 中的数组的一些操作是跟集合统一的
val arrayInt = arrayOf(1, 2, 3) // 类型会被推导为整型数组
val arrayString = arrayOf("apple", "pear") // 类型会被推导为字符串数组
println("Size is ${arrayInt.size}")
println("First element is ${arrayInt[0]}")
函数
函数声明 fun
//关键字 函数名 参数类型 返回值类型
//↓ ↓ ↓ ↓
fun helloFunction(name: String): String {
return "Hello $name !"
}
单一表达式函数:函数体只有一行代码
- 可以省略函数体的花括号,直接使用
=来连接 - 表达式当中的
return需要去掉 - 由于 Kotlin 支持类型推导,所以返回值的类型也可以省略
fun helloFunction(name: String) = "Hello $name !"
函数调用
Kotlin 支持命名参数,它允许我们在调用函数的时候传入形参的名字,这样的代码可读性更强。
helloFunction("Kotlin")
helloFunction(name = "Kotlin") // 只能是形参的名字 name
Kotlin 支持参数默认值,对于无默认值的参数,编译器会强制要求我们在调用处传参;对于有默认值的参数,则可传可不传。Kotlin 这样的特性,在参数较多的情况下有很大的优势。
fun createUser(
name: String,
age: Int,
count: Long = 0L,
size: Int = 0,
) = println("name=$name age=$age count=$count size=$size")
createUser("bqt", 30) // name=bqt age=30 count=0 size=0
createUser("bqt", 30, 10) // name=bqt age=30 count=10 size=0
createUser(age = 30, size = 10, name = "bqt") // name=bqt age=30 count=0 size=10
流程控制
在 Kotlin 当中,流程控制主要有 if、when、for、 while。
if
Kotlin 中的 if,除了可以作为程序语句(Statement),它还可以作为表达式(Expression)来使用。
val i = 1
val message = if (i > 0) "Big" else "Small"
var text: String? = null
val i = if (text != null) text.length else 0
val j = text?.length ?: 0 // Elvis 表达式
when
when 语句类似 Java 里的 switch case 语句,当代码大于两个逻辑分支的情况下,推荐使用 when。
val i: Int = 1
when(i) {
1 -> print("一")
2 -> print("二")
else -> print("不是一也不是二")
}
Kotlin 的 when 也可以作为表达式,为变量赋值。不过,与 switch 不一样的是,when 表达式要求它里面的逻辑分支必须是完整的。
val i: Int = 1
val message = when(i) {
1 -> "一"
2 -> "二"
else -> "i 不是一也不是二" // 如果去掉这行,会报错
}
while 与 for
Kotlin 中的 while 、do while 循环和 Java 中没有什么区别,但是 Kotlin 中的 for 语句和 Java 的用法就明显不一样了。
Kotlin 的 for 语句更多的是用于迭代数组、集合、区间。
val array = arrayOf(1, 2, 3)
for (i in array) {
println(i) // 迭代打印 array 数组里的所有元素
}
val oneToThree = 1..3 // 定义一个从 1 到 3 的闭区间(左闭右闭),代表 [1, 3]
for (i in oneToThree) {
println(i)
}
val reverse = 6 downTo 0 step 2 // 定义一个步长为 2 的逆序区间,即 6 4 2 0,不能使用【6..0】定义
小结
作为最负盛名的 IDE 的创造者,JetBrains 能深刻捕捉到开发者的需求。它知道开发者喜欢什么、讨厌什么,它甚至知道开发者容易犯什么样的错误,从而在语言设计的层面规避错误。
让开发者写出简洁易懂的代码:
- 支持类型推导
- 代码末尾不需要分号
- 字符串模板
- 原始字符串,支持复杂文本格式
- 单一表达式函数,简洁且符合直觉
- 函数参数支持默认值
- if 和 when 可以作为表达式
在语言设计的层面规避错误:
- 强制区分
可为空变量类型和不可为空变量类型,规避空指针异常 - 推崇不可变性,对于没有修改需求的变量,IDE 会智能提示开发者将
var改为val - 基础类型不支持
隐式类型转换,这能避免很多隐藏的问题 - 数组访问行为与集合统一,不会出现 array.length、list.size 这种恼人的情况
- 函数调用支持命名参数,提高可读性
- when 表达式,强制要求逻辑分支完整,让你写出来的逻辑永远不会有漏洞
2018-02-11