变量

1
2
3
4
5
6
/*
关键字 变量类型
↓ ↓ */
var price: Int = 100; /*
↑ ↑
变量名 变量值 */
1
var price: Int = 100

Kotlin 支持类型推导,大部分情况下,我们的变量类型可以省略不写

1
var price = 100 // 默认推导类型为: Int

我们应该尽可能避免使用 var,尽可能多地去使用 val

  • val 声明的变量,我们叫做不可变变量,它的值在初始化以后就无法再次被修改,它相当于 Java 里面的 final 变量。
  • var 声明的变量,我们叫做可变变量,它对应 Java 里的普通变量。

基础类型

在 Kotlin 语言体系当中,是没有原始类型这个概念的。这也就意味着,在 Kotlin 里,一切都是对象

虽然 Kotlin 在语法层面摒弃了“原始类型”,但有时候为了性能考虑,我们确实需要用“原始类型”。这时候我们应该怎么办?(TODO 后面会讲)

1
val i: Double = 1.toDouble()

空安全

1
2
val i: Double = null // 编译器报错
val j: Double? = null // 编译通过

由于 Kotlin 对可能为空的变量类型做了强制区分,这就意味着,“可能为空的变量”无法直接赋值给“不可为空的变量”,当然,反向赋值是没有问题的。

1
2
3
4
5
var i: Double = 1.0
var j: Double? = null

i = j // 编译器报错
j = i // 编译通过

数字类型

1
2
3
4
5
6
val int = 1
val long = 1234567L
val double = 13.14
val float = 13.14F
val hexadecimal = 0xAF
val binary = 0b01010101
  • 整数默认会被推导为“Int”类型;
  • Long 类型,我们则需要使用“L”后缀;
  • 小数默认会被推导为“Double”,我们不需要使用“D”后缀;
  • Float 类型,我们需要使用“F”后缀;
  • 使用“0x”,来代表十六进制字面量;
  • 使用“0b”,来代表二进制字面量。

Java 可以隐式转换数字类型,而 Kotlin 更推崇显式转换。

1
2
int i = 100;
long j = i;
1
2
3
4
5
val i = 100
val j: Long = i // 编译器报错

val i = 100
val j: Long = i.toLong() // 编译通过

Kotlin 这样设计的优势也是显而易见的,我们代码的可读性更强了,将来也更容易维护了。

字符:Char

1
2
3
4
5
val c: Char = 'A'
val i: Int = c // 编译器报错

val c: Char = 'A'
val i: Int = c.toInt() // 编译通过

字符串:String

1
val s = "Hello Kotlin!"

Kotlin 还为我们提供了非常简洁的字符串模板

1
2
3
4
5
6
7
val name = "Kotlin"
print("Hello $name!")
/* ↑
直接在字符串中访问变量
*/
// 输出结果:
Hello Kotlin!
1
2
3
4
5
6
7
val array = arrayOf("Java", "Kotlin")
print("Hello ${array.get(1)}!")
/* ↑
复杂的变量,使用${}
*/
// 输出结果:
Hello Kotlin!

Kotlin 还新增了一个原始字符串,它定义的时候是什么格式,最终打印也会是对应的格式

1
2
3
4
5
6
val s = """
当我们的字符串有复杂的格式时
原始字符串非常的方便
因为它可以做到所见即所得。 """

print(s)

数组

1
2
val arrayInt = arrayOf(1, 2, 3)
val arrayString = arrayOf("apple", "pear")

Kotlin 编译器也会根据传入的参数进行类型推导。

虽然 Kotlin 的数组仍然不属于集合,但它的一些操作是跟集合统一的。

1
2
3
4
5
6
7
val array = arrayOf("apple", "pear")
println("Size is ${array.size}")
println("First element is ${array[0]}")

// 输出结果:
Size is 2
First element is apple

函数声明

1
2
3
4
5
6
7
8
/*
关键字 函数名 参数类型 返回值类型
↓ ↓ ↓ ↓ */
fun helloFunction(name: String): String {
return "Hello $name !"
}/* ↑
花括号内为:函数体
*/

它的函数体实际上只有一行代码。那么针对这种情况,我们其实就可以省略函数体的花括号,直接使用“=”来连接,将其变成一种类似变量赋值的函数形式:

1
fun helloFunction(name: String): String = "Hello $name !"

称之为单一表达式函数

由于 Kotlin 支持类型推导,我们在使用单一表达式形式的时候,返回值的类型也可以省略:

1
fun helloFunction(name: String) = "Hello $name !"

函数调用

1
helloFunction("Kotlin")

Kotlin 提供了一些新的特性,那就是命名参数,在调用函数的时候传入“形参的名字”。

1
helloFunction(name = "Kotlin")

针对参数较多的函数,我们一般会以纵向的方式排列

1
2
3
4
5
6
7
8
9
10
11
fun createUser(
name: String,
age: Int,
gender: Int,
friendCount: Int,
feedCount: Int,
likeCount: Long,
commentCount: Int
) {
//..
}
1
2
3
4
5
6
7
8
9
createUser(
name = "Tom",
age = 30,
gender = 1,
friendCount = 78,
feedCount = 2093,
likeCount = 10937,
commentCount = 3285
)

体现出了 Kotlin 命名参数的可读性易维护性两个优势。

Kotlin 还支持参数默认值

1
2
3
4
5
6
7
8
9
10
11
fun createUser(
name: String,
age: Int,
gender: Int = 1,
friendCount: Int = 0,
feedCount: Int = 0,
likeCount: Long = 0L,
commentCount: Int = 0
) {
//..
}

对于有默认值的参数,则可传可不传

1
2
3
4
5
createUser(
name = "Tom",
age = 30,
commentCount = 3285
)

流程控制

if

它还可以作为表达式(Expression)来使用

1
2
3
4
5
6
7
val i = 1
val message = if (i > 0) "Big" else "Small"

print(message)

输出结果:
Big

我们会经常遇到可空的变量,并且要判断它们是否为空。

1
2
3
fun getLength(text: String?): Int {
return if (text != null) text.length else 0
}

Kotlin 针对这种情况就提供了一种简写,叫做 Elvis 表达式。

1
2
3
fun getLength(text: String?): Int {
return text?.length ?: 0
}

when

大于两个逻辑分支的情况下,我们使用 when

1
2
3
4
5
6
7
8
9
10
val i: Int = 1

when(i) {
1 -> print("一")
2 -> print("二")
else -> print("i 不是一也不是二")
}

输出结果:

它同时也可以作为表达式,为变量赋值

1
2
3
4
5
6
7
8
9
val i: Int = 1

val message = when(i) {
1 -> "一"
2 -> "二"
else -> "i 不是一也不是二" // 如果去掉这行,会报错
}

print(message)

when 表达式要求它里面的逻辑分支必须是完整的,如果去掉 else 分支,编译器将报错

循环迭代:while 与 for

Kotlin 的 for 语句更多的是用于“迭代”

1
2
3
4
val array = arrayOf(1, 2, 3)
for (i in array) {
println(i)
}

除了迭代数组和集合以外,Kotlin 还支持迭代一个“区间”。

1
val oneToThree = 1..3 // 代表 [1, 3]
1
2
3
4
5
6
7
8
for (i in oneToThree) {
println(i)
}

输出结果:
1
2
3

还可以逆序迭代一个区间

1
2
3
4
5
6
7
8
9
for (i in 6 downTo 0 step 2) {
println(i)
}

输出结果:
6
4
2
0

从 6 到 0,每次迭代的步长是 2,这意味着 6 迭代过后,到 4、2,最后到 0。
需要特别注意的是,逆序区间我们不能使用“6..0”来定义,如果用这样的方式来定义的话,代码将无法正常运行。

Kotlin优化点

  • 支持类型推导;
  • 代码末尾不需要分号;
  • 字符串模板;
  • 原始字符串,支持复杂文本格式;
  • 单一表达式函数,简洁且符合直觉;
  • 函数参数支持默认值,替代 Builder 模式的同时,可读性还很强;
  • if 和 when 可以作为表达式。

同时,JetBrains 也非常清楚开发者在什么情况下容易出错,所以,它在语言层面也做了很多改进:

  • 强制区分“可为空变量类型”和“不可为空变量类型”,规避空指针异常;
  • 推崇不可变性(val),对于没有修改需求的变量,IDE 会智能提示开发者将“var”改为“val”;
  • 基础类型不支持隐式类型转换,这能避免很多隐藏的问题;
  • 数组访问行为与集合统一,不会出现 array.length、list.size 这种恼人的情况;
  • 函数调用支持命名参数,提高可读性,在后续维护代码的时候不易出错;
  • when 表达式,强制要求逻辑分支完整,让你写出来的逻辑永远不会有漏洞。

关注我