变量
Kotlin 使用的是静态类型系统 (static type system),因为类型推断的语言特性,对于已声明并赋值的变量,它允许省略类型定义。
IDEA 中如果对一个变量的类型有疑问,可以单击变量名,并按 Control-Shift-P
组合键,会显示出变量的类型。
关键字
只能在变量前声明两种关键字
val
value 的缩写,用来声明一个不可变的变量,初始赋值之后不可重新赋值。对应 Java 中的 final 变量var
variable 的缩写,用来声明一个可变的变量,初始赋值之后仍可重新复制。对应 Java 中的非 final 变量
对一个变量延迟赋值,Kotlin 就无法自动推导它的类型了,可以显式地声明变量类型
fun main() {
println("Hello World")
var a: Int = 10
a *= 10
println("a = " + a)
println("a = $a")
}
数据类型
Java 和 Kotlin 数据类型对照
Java 基本数据类型 | Kotlin 对象数据类型 | 数据类型说明 |
---|---|---|
int | Int | 整形 |
long | Long | 长整形 |
short | Short | 短整形 |
float | Float | 单精度浮点型 |
double | Double | 双精度浮点型 |
boolean | Boolean | 布尔型 |
char | Char | 字符型 |
byte | Byte | 字节型 |
Kotlin 完全抛弃了 Java 中的基本数据类型,全部使用了数据对象类型。
val
的设计是为了解决 Java 中 final
关键字没有被合理使用的问题(大部分人没有主动使用)。永远优先使用 val 来声明一个变量,当 val 无法满足需求时再使用 var。
位运算
函数 | 描述 | 实例 |
---|---|---|
Integer.toBinaryString | 整数转二进制 | Integer.toBinaryString(12) |
shl(bitcount) | 按位左移 | 1.shl(2) 1 shl 2 |
shr(bitcount) | 按位右移 | 1.shr(2) 1 shr 2 |
inv() | 按位取反 | 1.inv() |
xor(number) | 按位异或 | 42.xor(33) 42 xor 33 |
and(number) | 按位与 | 42.and(10) 42 and 10 |
函数
Java 习惯叫做方法(翻译自 method),Kotlin 习惯叫做函数(翻译自 function)。
fun
是定义函数的关键字- fun 后面的是函数名
- 参数列表,参数的声明格式参数名: 参数类型
- 声明函数返回类型
import kotlin.math.max
fun main() {
val a = 37
val b = 40
val value = largerNumber(a, b)
println("The larger number is $value")
}
fun largerNumber(a: Int, b: Int): Int {
return max(a, b)
}
函数的参数默认值
Kotlin 提供了给函数设定参数默认值的功能。
fun main() {
printParams(10)
}
fun printParams(num: Int, str: String = "hello") {
println("num is $num, str is $str")
}
如果改成给第一个参数设定默认值,模仿刚才的写法,编译器会认为想把字符串赋值给第一个 num 参数,从而报类型不匹配的错误
fun main() {
// error! Type mismatch: inferred type is String but Int was expected
printParams("world")
}
fun printParams(num: Int = 5, str: String) {
println("num is $num, str is $str")
}
Kotlin 提供了通过键值对的方式来传参,不必按照函数参数列表中的参数顺序
fun main() {
printParams(str = "world")
printParams(str = "world", num = 100)
printParams(num = 1000, str = "world")
}
fun printParams(num: Int = 5, str: String) {
println("num is $num, str is $str")
}
单表达式函数
函数的语法糖,单表达式函数 (只有一个表达式语句,或者说都只有一条求值语句) 的返回类型、花括号、返回语句都可以省掉。允许不必编写函数体。
fun largerNumber(a: Int, b: Int) = max(a, b)
特殊字符命名的函数
Kotlin 可以定义或调用以空格和其他特殊字符命名的函数,函数名要用一对反引号括起来。
fun main() {
// Call
`**~prolly not a good idea!~**`()
}
fun `**~prolly not a good idea!~**`() {
println("**~prolly not a good idea!~**")
}
Kotlin 如此设计的原因
- Kotlin 和 Java 各自有不同的保留关键字,不能用作函数名。使用反引号括住函数名就能避免任何潜在冲突。
- 通过使用反引号特殊语法,可以在测试文件中使用更直观易懂的函数名。比单词以驼峰形式拼接的命名规则更加直观。
Unit 函数
Kotlin 使用 Unit 返回类型表明这样一件事:函数没有返回值。函数定义中如果没使用 return 关键字,那暗含的意思就是,Unit 就是这个函数的返回类型。
对于 Java 中的 void 关键字,意思是 “没有返回类型,不会带来什么,忽略它”。这似乎流于表面:如果函数不返回任何东西,就忽略类型。void 这种解决方案无法解释现代语言的一个重要特征:泛型。作为现代编译语言的一个特征,泛型让编程更为灵活。
Nothing 类型
Kotlin 还有一种叫作 Nothing 的类型。类似于 Unit,Nothing 类型的函数也不返回任何东西。
在编译器看来,Nothing 就意味着函数不可能成功执行完成,它要么抛出异常,要么因某个原因再也返不回调用处。
TODO 函数的任务就是抛出异常,返回Nothing类型。
看到 TODO 函数的 Nothing 类型,编译器就知道它肯定会抛错,所以就不会越过 TODO 函数去检查函数体内的返回类型,因为函数是不可能返回数据的。能通过编译器的检查,开发者就可以暂不具体实现函数,而是继续把该函数依赖的前提任务处理完毕。
逻辑控制
顺序语句、条件语句和循环语句
if 条件语句
Kotlin 中的 if 语句比 Java 有一个额外的功能,可以有返回值。
fun largerNumber(a: Int, b: Int): Int {
return if (a > b) {
a
} else {
b
}
}
if 语句使用每个条件的最后一行作为返回值。
fun largerNumber(a: Int, b: Int) = if (a > b) a else b
when 条件语句
if、when 都可以有返回值,when 比 Java 中的 switch 要好用。when 的逻辑表现力更强,且配合单行代码函数的语法糖使用,代码更简洁。
fun getScore(name: String) = when (name) {
"Tom" -> 86
"Jim" -> 77
"Lily" -> 100
else -> 0
}
when 语句允许传入一个任意类型的参数,然后在 when 的结构体中定义一系列的条件,执行逻辑只有一行时,{}
可以省略。
匹配值 -> { 执行逻辑 }
when 语句允许进行类型判断。
fun checkNumber(num: Number) = when (num) {
is Int -> println("number is Int")
is Double -> println("number is Double")
else -> println("number not support")
}
is
相当于 Java 中的 instanceof
关键字。
when 语句还有一种不带参数的用法,不常用但是能发挥出扩展性。
fun getScore(name: String) = when {
name.startsWith("Tom") -> 86
name == "Jim" -> 77
name == "Lily" -> 100
else -> 0
}
Kotlin 中判断字符串或者对象相等可以直接使用 ==
。
循环语句
while 循环与 Java 中没有任何区别,跳过。
Java 中的 for-i
循环被舍弃了,for-each
循环被增强,变为 Kotlin 中的 for-in
循环。
区间
Kotlin 中使用 a .. b
可以创建一个双端闭区间([a, b]),使用 a until b
可以创建一个单端闭区间([a, b))。
fun main() {
for (i in 0..10) {
println(i)
}
for (i in 0 until 10) {
println(i)
}
}
..
与 until
都要求左端小于等于右端,如果需要降序区间,可以使用 downTo
来创建。比如 10 downTo 1
相当于 [10, 1]
的降序区间。
fun main() {
for (i in 10 downTo 1) {
println(i)
}
}
for-in
每次循环时都会在区间范围内递增1,类似于 for-i
中的 i++
。
如果想多跳过其中的一些元素,可以使用 step
关键字。
fun main() {
for (i in 0..10 step 2) {
println(i)
}
for (i in 0 until 10 step 3) {
println(i)
}
for (i in 10 downTo 1 step 4) {
println(i)
}
}
for-in
没有传统的 for-i
灵活,但是简单好用,覆盖了绝大部分使用场景。如果一些特殊场景无法实现, 可以改用 while 循环的方式进行。