Kotlin 基础知识

简介

在此 Codelab 中,您将了解 Kotlin 编程语言的基础知识:数据类型、运算符、变量、控制结构,以及可为 null 的变量与不可为 null 的变量。

您应当已掌握的内容

  • 如何在 IntelliJ IDEA 中创建项目
  • 如何在 IntelliJ IDEA 中打开并执行 Kotlin REPL(读取-求值-输出循环)中的代码 (Tools > Kotlin > Kotlin REPL)

学习内容

  • 如何使用 Kotlin 数据类型、运算符和变量
  • 如何使用布尔值和条件
  • 可为 null 的变量与不可为 null 的变量之间的区别
  • 数组、列表和循环在 Kotlin 中的运作方式

实践内容

  • 使用 Kotlin REPL 了解 Kotlin 的基础知识

在此任务中,您将了解 Kotlin 编程语言中的运算符和类型。

第 1 步:探索数值运算符

  1. 打开 IntelliJ IDEA(如果尚未打开)。
  2. 如需打开 Kotlin REPL,请依次选择 Tools > Kotlin > Kotlin REPL2b1c3edac8da7c7.png

与其他语言一样,Kotlin 使用 +-*/ 表示加法、减法、乘法和除法运算。此外,Kotlin 还支持不同的数字类型,如 IntLongDoubleFloat

  1. 在 REPL 中输入以下表达式。如需查看结果,请在输入每个表达式后按 Control+Enter(在 Mac 上,按 Command+Enter)。
1+1
⇒ res8: kotlin.Int = 2

53-3
⇒ res9: kotlin.Int = 50

50/10
⇒ res10: kotlin.Int = 5

1.0/2.0
⇒ res11: kotlin.Double = 0.5

2.0*3.5
⇒ res12: kotlin.Double = 7.0

请注意,运算结果会保留运算数的类型,因此 1/2 = 0,但 1.0/2.0 = 0.5。

  1. 尝试一些使用不同整数和小数组合的表达式。
6*50
⇒ res13: kotlin.Int = 300

6.0*50.0
⇒ res14: kotlin.Double = 300.0

6.0*50
⇒ res15: kotlin.Double = 300.0
  1. 对数字调用一些方法。Kotlin 将数字保留为基元,但允许您对数字调用方法,就像数字是对象一样。
2.times(3)
⇒ res5: kotlin.Int = 6

3.5.plus(4)
⇒ res8: kotlin.Double = 7.5

2.4.div(2)
⇒ res9: kotlin.Double = 1.2

第 2 步:练习使用类型

利用 Kotlin 无法执行隐式类型转换。因此,您无法直接为长变量赋予短值,也无法为 Long 赋予 Int。这是因为隐式数字转换是程序中常见的错误来源。您始终可以通过类型转换赋予不同类型的值。

  1. 如需查看部分可能的类型转换,请在 REPL 中定义 Int 类型的变量。
val i: Int = 6
  1. 创建一个新变量,然后输入上面显示的变量名称,后跟 .to
val b1 = i.to

IntelliJ IDEA 会显示可能的补全列表。这种自动补全功能适用于任何类型的变量和对象。6495b0b44b910418.png

  1. 从该列表中选择 toByte(),然后输出变量。
val b1 = i.toByte()
println(b1)
⇒ 6
  1. 为不同类型的变量赋予 Byte 值。
val b2: Byte = 1 // OK, literals are checked statically
println(b2)
⇒ 1

val i1: Int = b2
⇒ error: type mismatch: inferred type is Byte but Int was expected

val i2: String = b2
⇒ error: type mismatch: inferred type is Byte but String was expected

val i3: Double = b2
⇒ error: type mismatch: inferred type is Byte but Double was expected
  1. 对于返回错误的赋值,请尝试改为对它们进行类型转换。
val i4: Int = b2.toInt() // OK!
println(i4)
⇒ 1

val i5: String = b2.toString()
println(i5)
⇒ 1

val i6: Double = b2.toDouble()
println(i6)
⇒ 1.0
  1. 如需提高长数字常量的可读性,利用 Kotlin 可以对您有意义的方式在数字中添加下划线。请尝试输入不同的数字常量。
val oneMillion = 1_000_000
val socialSecurityNumber = 999_99_9999L
val hexBytes = 0xFF_EC_DE_5E
val bytes = 0b11010010_01101001_10010100_10010010

第 3 步:了解变量类型的值

Kotlin 支持可更改(可变)和不可更改(不可变)这两种类型的变量,分别使用 varval 指定。使用 val 时,您可以赋值一次。如果您尝试再次赋值,就会收到错误消息。使用 var 时,您可以先赋值,稍后再在程序中更改该值。

  1. 使用 valvar 定义变量,然后为其赋予新值。
var fish = 1
fish = 2
val aquarium = 1
aquarium = 2
⇒ error: val cannot be reassigned

您可以先为 fish 赋予一个值,然后再为其赋予一个新值,因为它是使用 var 定义的。尝试为 aquarium 赋予一个新值会出现错误,因为它是使用 val 定义的。

当编译器可以根据上下文推断变量时,系统就会推断您存储在该变量中的类型。如有需要,您可以随时使用冒号表示法明确指定变量的类型。

  1. 定义一些变量并明确指定变量的类型。
var fish: Int = 12
var lakes: Double = 2.5

您或编译器赋予某种类型后,您便无法更改该类型,或者会收到错误消息。

第 4 步:了解字符串和字符

Kotlin 中的字符串是字符序列。通过使用 " 表示字符串,使用 ' 表示单个字符,您可以将 Kotlin 中的字符串定义为类似于其他编程语言中的字符串。您可以使用 + 运算符连接字符串。此外,利用 Kotlin 还可以在字符串字面量中使用字符串模板表达式。这些模板是经求值并连接到字符串字面量中的代码片段。字符串模板以“$”开头,后跟 Kotlin 表达式,称为字符串插值。

  1. 创建一个字符串模板。
val numberOfFish = 5
val numberOfPlants = 12
"I have $numberOfFish fish" + " and $numberOfPlants plants"
⇒ res20: kotlin.String = I have 5 fish and 12 plants
  1. 创建一个包含表达式的字符串模板。与在其他语言中一样,该值可以是表达式的结果。使用大括号 {} 定义该表达式。
"I have ${numberOfFish + numberOfPlants} fish and plants"
⇒ res21: kotlin.String = I have 17 fish and plants

在此任务中,您将了解 Kotlin 编程语言中的布尔值和检查条件。与其他语言一样,Kotlin 具有布尔值和布尔运算符,如小于、等于、大于等(<==>!=<=>=)。

  1. 编写 if/else 语句。
val numberOfFish = 50
val numberOfPlants = 23
if (numberOfFish > numberOfPlants) {
    println("Good ratio!")
} else {
    println("Unhealthy ratio")
}
⇒ Good ratio!
  1. 利用 Kotlin 可以轻松定义一系列具有起始端点和终止端点的值,称为范围。在 Kotlin 中创建范围的最简单方法是使用“..”运算符。请尝试在 if 语句中使用范围。在 Kotlin 中,测试的条件也可以使用范围。
val fish = 50
if (fish in 1..100) {
    println(fish)
}
⇒ 50
  1. 编写一个包含多个案例的 if 语句。对于更复杂的条件,请使用逻辑 AND && 和逻辑 OR ||。与在其他语言中一样,您可以使用 else if 包含多个案例。
if (numberOfFish == 0) {
    println("Empty tank")
} else if (numberOfFish < 40) {
    println("Got fish!")
} else {
    println("That's a lot of fish!")
}
⇒ That's a lot of fish!
  1. 请尝试使用 when 语句。在 Kotlin 中,使用 when 语句编写该系列 if/else 语句很方便。when 语句类似于其他语言中的 switch 语句。when 语句中的条件也可以使用范围。
when (numberOfFish) {
    0  -> println("Empty tank")
    in 1..39 -> println("Got fish!")
    else -> println("That's a lot of fish!")
}
⇒ That's a lot of fish!

在此任务中,您将了解可为 null 的变量和不可为 null 的变量。涉及 null 的编程错误是无数错误的根源。Kotlin 致力于通过引入不可为 null 的变量来减少错误。

第 1 步:了解是否可为 null 性

默认情况下,变量不能为 null

  1. 声明 Int 并为其赋予 null
var rocks: Int = null
⇒ error: null can not be a value of a non-null type Int
  1. 在类型后使用问号运算符 ? 指示变量可为 null。声明 Int? 并为其赋予 null
var marbles: Int? = null

当您拥有复杂的数据类型(如列表)时:

  • 可以允许列表的元素为 null。
  • 可以允许列表为 null,但如果列表不为 null,则其元素不能为 null。
  • 可以允许列表或元素为 null。

列表和一些其他复杂的数据类型将在后续任务中介绍。

第 2 步:了解 ? 和 ?: 运算符

您可以使用 ? 运算符测试 null,这样可免于编写多个 if/else 语句。

  1. 以更长的方式编写某些代码,以检查 fishFoodTreats 变量是否不为 null。然后,递减该变量。
var fishFoodTreats = 6
if (fishFoodTreats != null) {
    fishFoodTreats = fishFoodTreats.dec()
}
  1. 现在,让我们看看 Kotlin 的编写方法,即使用 ? 运算符。
var fishFoodTreats = 6
fishFoodTreats = fishFoodTreats?.dec()
  1. 此外,您也可以使用 ?: 运算符链接 null 测试。看看下面这个示例:
fishFoodTreats = fishFoodTreats?.dec() ?: 0

上面的示例是“如果 fishFoodTreats 不为 null,则递减并使用该变量;否则,使用 ?: 后的值,即 0”的简写形式。如果 fishFoodTreatsnull,系统会终止求值,并且不会调用 dec() 方法。

关于 null 指针的要点

如果您确实喜欢 NullPointerExceptions,在 Kotlin 中,您可以保留它们。非 null 断言运算符 !! (!!) 会将任何值转换为非 null 类型,并在值为 null 时抛出异常。

val len = s!!.length   // throws NullPointerException if s is null

在此任务中,您将了解数组和列表,并了解使用 Kotlin 编程语言创建循环的不同方法。

第 1 步:创建列表

列表是 Kotlin 中的一种基本类型,类似于其他语言中的列表。

  1. 使用 listOf 声明一个列表并输出该列表。此列表无法更改。
val school = listOf("mackerel", "trout", "halibut")
println(school)
⇒ [mackerel, trout, halibut]
  1. 使用 mutableListOf 声明一个可以更改的列表。移除项。
val myList = mutableListOf("tuna", "salmon", "shark")
myList.remove("shark")
⇒ res36: kotlin.Boolean = true

remove() 方法会在成功移除传递的项后返回 true

第 2 步:创建数组

与其他语言一样,Kotlin 具有数组。在 Kotlin 中,列表具有可变版本和不可变版本,与之不同,Array 没有可变版本。数组一经创建,其大小便固定不变。除非复制到新数组中,否则无法添加或移除元素。

使用 valvar 的规则与数组和列表相同。

  1. 使用 arrayOf 声明一个字符串数组。使用 java.util.Arrays.toString() 数组实用程序进行输出。
val school = arrayOf("shark", "salmon", "minnow")
println(java.util.Arrays.toString(school))
⇒ [shark, salmon, minnow]
  1. 使用 arrayOf 声明的数组的类型与元素不相关,因此您可以使用混合类型,这会很有帮助。声明具有不同类型的数组。
val mix = arrayOf("fish", 2)
  1. 此外,您还可以为所有元素声明一种类型的数组。使用 intArrayOf() 声明一个整数数组。对于其他类型的数组,存在相应的构建器或实例化函数。
val numbers = intArrayOf(1,2,3)
  1. 使用 + 运算符组合两个数组。
val numbers = intArrayOf(1,2,3)
val numbers3 = intArrayOf(4,5,6)
val foo2 = numbers3 + numbers
println(foo2[5])
=> 3
  1. 尝试嵌套数组和列表的不同组合。与在其他语言中一样,您可以嵌套数组和列表。换句话说,当您将一个数组放入另一个数组时,您将得到一系列数组,而不是这两个数组内容的扁平化数组。此外,数组的元素可以是列表,而列表的元素可以是数组。
val numbers = intArrayOf(1, 2, 3)
val oceans = listOf("Atlantic", "Pacific")
val oddList = listOf(numbers, oceans, "salmon")
println(oddList)
⇒ [[I@89178b4, [Atlantic, Pacific], salmon]

第一个元素 numbersArray。如果您不使用数组实用程序输出它,Kotlin 会输出地址而不是数组内容。

  1. Kotlin 的一个优点是,您可以使用代码初始化数组,而不是将数组初始化为 0。不妨试试下面这个示例:
val array = Array (5) { it * 2 }
println(java.util.Arrays.toString(array))
⇒ [0, 2, 4, 6, 8]

初始化代码使用大括号 {} 括起来。在该代码中,it 表示以 0 开头的数组索引。

第 3 步:创建循环

创建列表和数组后,您现在可以按照预期的方式循环遍历元素。

  1. 创建一个数组。使用 for 循环可遍历数组并输出元素。
val school = arrayOf("shark", "salmon", "minnow")
for (element in school) {
    print(element + " ")
}
⇒ shark salmon minnow
  1. 在 Kotlin 中,您可以同时循环遍历元素和索引。不妨试试下面这个示例:
for ((index, element) in school.withIndex()) {
    println("Item at $index is $element\n")
}
⇒ Item at 0 is shark
Item at 1 is salmon
Item at 2 is minnow
  1. 尝试不同的步长和范围。您可以指定数字范围或按字母顺序指定字符范围。与在其他语言中一样,不一定要采用步长为 1 的递增范围。您可以通过 downTo 采用递减范围。
for (i in 1..5) print(i)
⇒ 12345

for (i in 5 downTo 1) print(i)
⇒ 54321

for (i in 3..6 step 2) print(i)
⇒ 35

for (i in 'd'..'g') print (i)
⇒ defg
  1. 尝试一些循环。与其他语言一样,Kotlin 具有 while 循环、do...while 循环以及 ++-- 运算符。此外,Kotlin 还具有 repeat 循环。
var bubbles = 0
while (bubbles < 50) {
    bubbles++
}
println("$bubbles bubbles in the water\n")

do {
    bubbles--
} while (bubbles > 50)
println("$bubbles bubbles in the water\n")

repeat(2) {
     println("A fish is swimming")
}
⇒ 50 bubbles in the water
49 bubbles in the water
A fish is swimmingA fish is swimming

Kotlin 在运算符、列表、循环等基础知识方面与其他语言非常相似,但存在一些显著区别。

在 Kotlin 中,以下功能与您在其他语言中习惯使用的功能可能会有所不同:

  • Kotlin 类型无法隐式转换,请使用类型转换。
  • 使用 val 声明的变量只能赋值一次。
  • 默认情况下,Kotlin 变量不可为 null。使用 ? 使变量可为 null。
  • 利用 Kotlin 可以在 for 循环中同时循环遍历数组的索引和元素。

以下 Kotlin 编程结构与其他语言的编程结构类似:

  • 数组和列表可以是单一类型,也可以是混合类型。
  • 数组和列表可以进行嵌套。
  • 您可以使用 forwhiledo/whilerepeat 创建循环。
  • when 语句是 Kotlin 版本的 switch 语句,但 when 更灵活。