1. 欢迎
此代码实验室是 Kotlin 程序员训练营课程 的一部分。如果您按顺序完成代码实验室,将获得最大的课程价值。根据您的知识水平,您可能可以略过某些部分。本课程面向了解面向对象语言并希望学习 Kotlin 的程序员。
简介
在本代码实验室中,您将学习 Kotlin 编程语言的基础知识:数据类型、运算符、变量、控制结构以及可空变量与不可空变量。本课程面向了解面向对象语言并希望学习 Kotlin 的程序员。
本课程中的教程并非构建单个示例应用,而是旨在构建您的知识,但彼此之间相对独立,因此您可以略过您熟悉的章节。为了将它们联系起来,许多示例都使用了水族馆主题。如果您想了解完整的“水族馆故事”,请查看 Kotlin 程序员训练营 Udacity 课程。
您应该具备的知识
- 如何在 IntelliJ IDEA 中创建项目
- 如何在 IntelliJ IDEA 中打开并在 Kotlin 的 REPL(读取-评估-打印循环)中执行代码(**工具 > Kotlin > Kotlin REPL**)
您将学到什么
- 如何使用 Kotlin 数据类型、运算符和变量
- 如何使用布尔值和条件
- 可空变量和不可空变量之间的区别
- Kotlin 中的数组、列表和循环的工作原理
您将做什么
- 使用 Kotlin REPL 学习 Kotlin 基础知识
2. 任务:了解运算符和类型
在本任务中,您将学习 Kotlin 编程语言中的运算符和类型。
步骤 1:探索数值运算符
- 打开 IntelliJ IDEA(如果尚未打开)。
- 要打开 Kotlin REPL,请选择**工具** > **Kotlin** > **Kotlin REPL**。
与其他语言一样,Kotlin 使用 +
、-
、*
和 /
表示加、减、乘和除。Kotlin 还支持不同的数字类型,例如 Int
、Long
、Double
和 Float
。
- 在 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。
- 尝试使用整数和小数的不同组合进行一些表达式。
6*50 ⇒ res13: kotlin.Int = 300 6.0*50.0 ⇒ res14: kotlin.Double = 300.0 6.0*50 ⇒ res15: kotlin.Double = 300.0
- 对数字调用一些方法。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 不会在数字类型之间进行隐式转换,因此您不能将 short 值直接赋值给 long 变量,或将 Byte
赋值给 Int
。这是因为隐式数字转换是程序中常见的错误来源。您始终可以通过强制转换来赋值不同类型的变量。
- 要查看一些可能的强制转换,请在 REPL 中定义一个类型为
Int
的变量。
val i: Int = 6
- 创建一个新变量,然后输入上面显示的变量名,后跟
.to
。
val b1 = i.to
IntelliJ IDEA 会显示一个可能的完成列表。此自动完成功能在任何类型的变量和对象上都有效。
- 从列表中选择
toByte()
,然后打印变量。
val b1 = i.toByte()
println(b1)
⇒ 6
- 将
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
- 对于返回错误的赋值,请尝试改为强制转换。
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
- 为了使长数值常量更易于阅读,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 支持两种类型的变量:可变和不可变。使用 val
,您可以只赋值一次。如果尝试再次赋值,则会发生错误。使用 var
,您可以赋值,然后在程序的后面更改该值。
- 使用
val
和var
定义变量,然后为其赋值。
var fish = 1
fish = 2
val aquarium = 1
aquarium = 2
⇒ error: val cannot be reassigned
您可以为 fish
赋值,然后为其重新赋值,因为它是使用 var
定义的。尝试为 aquarium
重新赋值会导致错误,因为它使用 val
定义。
当编译器可以从上下文中推断出类型时,就会推断出存储在变量中的类型。如果需要,您始终可以使用冒号表示法显式指定变量的类型。
- 定义一些变量并显式指定类型。
var fish: Int = 12
var lakes: Double = 2.5
一旦您或编译器分配了类型,就不能更改类型,否则会发生错误。
步骤 4:了解字符串
Kotlin 中的字符串的工作方式与任何其他编程语言中的字符串非常相似,使用 "
表示字符串,使用 '
表示单个字符,并且可以使用 +
运算符连接字符串。您可以通过将字符串与值组合来创建字符串模板;$
变量
名称将替换为表示该值的文本。这称为变量插值。
- 创建字符串模板。
val numberOfFish = 5
val numberOfPlants = 12
"I have $numberOfFish fish" + " and $numberOfPlants plants"
⇒ res20: kotlin.String = I have 5 fish and 12 plants
- 创建包含表达式的字符串模板。与其他语言一样,该值可以是表达式的结果。使用花括号
{}
定义表达式。
"I have ${numberOfFish + numberOfPlants} fish and plants"
⇒ res21: kotlin.String = I have 17 fish and plants
3. 任务:比较条件和布尔值
在本任务中,您将学习 Kotlin 编程语言中的布尔值和条件检查。与其他语言一样,Kotlin 具有布尔值和布尔运算符,例如小于、等于、大于等(<
、==
、>
、!=
、<=
、>=
)。
- 编写
if
/else
语句。
val numberOfFish = 50
val numberOfPlants = 23
if (numberOfFish > numberOfPlants) {
println("Good ratio!")
} else {
println("Unhealthy ratio")
}
⇒ Good ratio!
- 在
if
语句中尝试一个范围。在 Kotlin 中,您测试的条件也可以使用范围。
val fish = 50
if (fish in 1..100) {
println(fish)
}
⇒ 50
- 编写一个包含多个情况的
if
。对于更复杂的条件,请使用逻辑与&&
和逻辑或||
。与其他语言一样,您可以使用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!
- 试用
when
语句。在 Kotlin 中,有一种更简洁的方式来编写一系列if
/else 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!
4. 任务:了解可空性
在本任务中,您将学习可空变量与不可空变量。涉及空值的编程错误一直是无数错误的根源。Kotlin 试图通过引入不可空变量来减少错误。
步骤 1:了解可空性
默认情况下,变量不能为 null
。
- 声明一个
Int
并将其赋值为null
。
var rocks: Int = null
⇒ error: null can not be a value of a non-null type Int
- 在类型后面使用问号运算符
?
表示变量可以为 null。声明一个Int?
并将其赋值为null
。
var marbles: Int? = null
当您拥有复杂的数据类型(例如列表)时
- 您可以允许列表的元素为 null。
- 您可以允许列表为 null,但如果它不为 null,则其元素不能为 null。
- 您可以允许列表或元素都为 null。
列表和某些其他复杂数据类型将在后面的任务中介绍。
步骤 2:了解 ? 和 ?: 运算符
您可以使用 ?
运算符测试 null
,从而避免编写许多 if
/else
语句。
- 编写一些更长的代码来检查
fishFoodTreats
变量是否不为null
。然后递减该变量。
var fishFoodTreats = 6
if (fishFoodTreats != null) {
fishFoodTreats = fishFoodTreats.dec()
}
- 现在看看 Kotlin 的编写方式,使用
?
运算符。
var fishFoodTreats = 6
fishFoodTreats = fishFoodTreats?.dec()
- 您还可以使用
?:
运算符链接空值测试。请查看此示例
fishFoodTreats = fishFoodTreats?.dec() ?: 0
它是“如果 fishFoodTreats
不为 null
,则递减并使用它;否则使用 ?:
后面的值,即 0”的简写。如果 fishFoodTreats
为 null
,则评估停止,并且不会调用 dec()
方法。
关于空指针的一点说明
如果你真的喜欢 NullPointerExceptions
,Kotlin 允许你保留它们。非空断言运算符 !!
(双感叹号)将任何值转换为非空类型,如果该值为 null
,则抛出异常。
val len = s!!.length // throws NullPointerException if s is null
5. 任务:探索数组、列表和循环
在这个任务中,你将学习数组和列表,以及学习在 Kotlin 编程语言中创建循环的不同方法。
步骤 1:创建列表
列表是 Kotlin 中的基本类型,类似于其他语言中的列表。
- 使用
listOf
声明一个列表并打印出来。此列表不可更改。
val school = listOf("mackerel", "trout", "halibut")
println(school)
⇒ [mackerel, trout, halibut]
- 使用
mutableListOf
声明一个可以更改的列表。删除一个项目。
val myList = mutableListOf("tuna", "salmon", "shark")
myList.remove("shark")
⇒ res36: kotlin.Boolean = true
当 remove()
方法成功删除传递的项目时,它将返回 true
。
步骤 2:创建数组
与其他语言一样,Kotlin 也有 数组。与 Kotlin 中的列表(有可变和不可变版本)不同,Array
没有可变版本。创建数组后,大小是固定的。你不能添加或删除元素,除非复制到一个新数组。
使用 val
和 var
的规则与数组和列表相同。
- 使用
arrayOf
声明一个字符串数组。使用java.util.Arrays.toString()
数组实用程序打印出来。
val school = arrayOf("shark", "salmon", "minnow")
println(java.util.Arrays.toString(school))
⇒ [shark, salmon, minnow]
- 使用
arrayOf
声明的数组没有与元素关联的类型,因此你可以混合类型,这很有帮助。声明一个具有不同类型的数组。
val mix = arrayOf("fish", 2)
- 你还可以为所有元素声明一个类型的数组。使用
intArrayOf()
声明一个整数数组。对于其他类型的数组,有相应的构建器或实例化函数。
val numbers = intArrayOf(1,2,3)
- 使用
+
运算符组合两个数组。
val numbers = intArrayOf(1,2,3)
val numbers3 = intArrayOf(4,5,6)
val foo2 = numbers3 + numbers
println(foo2[5])
=> 3
- 尝试嵌套数组和列表的不同组合。与其他语言一样,你可以嵌套数组和列表。也就是说,当你将一个数组放入另一个数组中时,你得到的是一个数组的数组——而不是两个数组内容的扁平化数组。数组的元素也可以是列表,列表的元素也可以是数组。
val numbers = intArrayOf(1, 2, 3)
val oceans = listOf("Atlantic", "Pacific")
val oddList = listOf(numbers, oceans, "salmon")
println(oddList)
⇒ [[I@89178b4, [Atlantic, Pacific], salmon]
第一个元素 numbers
是一个 Array
。当你没有使用数组实用程序打印它时,Kotlin 会打印地址而不是数组的内容。
- Kotlin 的一个不错的特性是,你可以使用代码初始化数组,而不是将它们初始化为 0。尝试此示例
val array = Array (5) { it * 2 }
println(java.util.Arrays.toString(array))
⇒ [0, 2, 4, 6, 8]
初始化代码位于花括号 {}
之间。在代码中,it
指的是数组索引,从 0 开始。
步骤 3:创建循环
现在你有了列表和数组,遍历元素的工作方式和你预期的一样。
- 创建一个数组。使用
for
循环遍历数组并打印元素。
val school = arrayOf("shark", "salmon", "minnow")
for (element in school) {
print(element + " ")
}
⇒ shark salmon minnow
- 在 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 为步长向前移动。你可以使用
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
- 尝试一些循环。与其他语言一样,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 swimming A fish is swimming
6. 总结
在运算符、列表和循环等基本方面,Kotlin 与其他语言非常相似,但有一些重要的区别。
以下特性在 Kotlin 中可能与你在其他语言中习惯的不同
- Kotlin 类型不能隐式转换——使用强制转换。
- 使用
val
声明的变量只能赋值一次。 - 默认情况下,Kotlin 变量不可为空。使用
?
使变量可为空。 - 使用 Kotlin,你可以在
for
循环中同时遍历数组的索引和元素。
以下 Kotlin 编程结构与其他语言中的类似
- 数组和列表可以具有单一类型或混合类型。
- 数组和列表可以嵌套。
- 你可以使用
for
、while
、do
/while
和repeat
创建循环。 when
语句是 Kotlin 中的switch
语句版本,但when
更加灵活。
7. 了解更多
Kotlin 文档
如果你想获得本课程中任何主题的更多信息,或者如果你遇到困难,https://kotlinlang.org 是你最佳的起点。
Kotlin 教程
https://play.kotlinlang.org 网站包含丰富的教程,称为 Kotlin Koans,一个 基于 Web 的解释器,以及一套完整的带有示例的参考文档。
Udacity 课程
要查看有关此主题的 Udacity 课程,请参阅 Kotlin Bootcamp for Programmers。
IntelliJ IDEA
可以在 JetBrains 网站上找到 IntelliJ IDEA 的文档。
8. 作业
本节列出了作为课程一部分完成本代码实验室的学生的可能作业。由讲师决定以下事项
- 如果需要,分配作业。
- 告知学生如何提交作业。
- 批改作业。
讲师可以根据需要使用这些建议,并且可以随意分配他们认为合适的任何其他作业。
如果你自己正在学习本代码实验室,可以随意使用这些作业来测试你的知识。
回答这些问题
问题 1
以下哪项声明了一个不可更改的字符串列表?
▢ val school = arrayOf("shark", "salmon", "minnow")
▢ var school = arrayOf("shark", "salmon", "minnow")
▢ val school = listOf("shark", "salmon", "minnow")
▢ val school = mutableListOf("shark", "salmon", "minnow")
问题 2
以下代码的输出是什么?for (i in 3..8 step 2) print(i)
▢ 345678
▢ 468
▢ 38
▢ 357
问题 3
此代码中问号的作用是什么?var rocks: Int? = 3
▢ 变量 rocks
的类型未固定。
▢ 变量 rocks
可以设置为 null。
▢ 变量 rocks
不能设置为 null。
▢ 变量 rocks
不应该立即初始化。
9. 下一个代码实验室
有关课程概述(包括其他代码实验室的链接),请参阅 “Kotlin Bootcamp for Programmers:欢迎来到课程。”