Kotlin 程序员训练营 2:Kotlin 基础

1. 欢迎

此代码实验室是 Kotlin 程序员训练营课程 的一部分。如果您按顺序完成代码实验室,将获得最大的课程价值。根据您的知识水平,您可能可以略过某些部分。本课程面向了解面向对象语言并希望学习 Kotlin 的程序员。

sEioGm-YJlcEfGjX0S6M-MQDi23k2ZjQCNPkuImT4e5BIqCJ7XCoLqvDJlUK4cB9XfffJQOcpcW_I8J1LRpYN6qk_b7NMWSQi_0yAWk6Gm5e9C-vvNo5v8geG9iINqKPc_byPxgqMA

简介

在本代码实验室中,您将学习 Kotlin 编程语言的基础知识:数据类型、运算符、变量、控制结构以及可空变量与不可空变量。本课程面向了解面向对象语言并希望学习 Kotlin 的程序员。

本课程中的教程并非构建单个示例应用,而是旨在构建您的知识,但彼此之间相对独立,因此您可以略过您熟悉的章节。为了将它们联系起来,许多示例都使用了水族馆主题。如果您想了解完整的“水族馆故事”,请查看 Kotlin 程序员训练营 Udacity 课程。

BLgezynJ_92kR7lbZPbmkh7cDUCFMm3Ugo_JUOdDd5IpMdkk8nu3nbMiSkQWK5dx4-NX4qlbUXwU9l_Pj_7QqoRSUX2YbiddIUO9I100elofv-IY6xAHo7RL9CCXnjEwBKyLknPHzw

您应该具备的知识

  • 如何在 IntelliJ IDEA 中创建项目
  • 如何在 IntelliJ IDEA 中打开并在 Kotlin 的 REPL(读取-评估-打印循环)中执行代码(**工具 > Kotlin > Kotlin REPL**)

您将学到什么

  • 如何使用 Kotlin 数据类型、运算符和变量
  • 如何使用布尔值和条件
  • 可空变量和不可空变量之间的区别
  • Kotlin 中的数组、列表和循环的工作原理

您将做什么

  • 使用 Kotlin REPL 学习 Kotlin 基础知识

2. 任务:了解运算符和类型

在本任务中,您将学习 Kotlin 编程语言中的运算符和类型。

步骤 1:探索数值运算符

  1. 打开 IntelliJ IDEA(如果尚未打开)。
  2. 要打开 Kotlin REPL,请选择**工具** > **Kotlin** > **Kotlin REPL**。2b1c3edac8da7c7.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 不会在数字类型之间进行隐式转换,因此您不能将 short 值直接赋值给 long 变量,或将 Byte 赋值给 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 支持两种类型的变量:可变和不可变。使用 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 中的字符串的工作方式与任何其他编程语言中的字符串非常相似,使用 " 表示字符串,使用 ' 表示单个字符,并且可以使用 + 运算符连接字符串。您可以通过将字符串与值组合来创建字符串模板;$变量名称将替换为表示该值的文本。这称为变量插值

  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

3. 任务:比较条件和布尔值

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

  1. 编写 if/else 语句。
val numberOfFish = 50
val numberOfPlants = 23
if (numberOfFish > numberOfPlants) {
    println("Good ratio!") 
} else {
    println("Unhealthy ratio")
}
⇒ Good ratio!
  1. if 语句中尝试一个范围。在 Kotlin 中,您测试的条件也可以使用范围。
val fish = 50
if (fish in 1..100) {
    println(fish)
}
⇒ 50
  1. 编写一个包含多个情况的 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!
  1. 试用 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

  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. 您还可以使用 ?: 运算符链接空值测试。请查看此示例

fishFoodTreats = fishFoodTreats?.dec() ?: 0

它是“如果 fishFoodTreats 不为 null,则递减并使用它;否则使用 ?: 后面的值,即 0”的简写。如果 fishFoodTreatsnull,则评估停止,并且不会调用 dec() 方法。

关于空指针的一点说明

如果你真的喜欢 NullPointerExceptions,Kotlin 允许你保留它们。非空断言运算符 !!(双感叹号)将任何值转换为非空类型,如果该值为 null,则抛出异常。

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

5. 任务:探索数组、列表和循环

在这个任务中,你将学习数组和列表,以及学习在 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]

第一个元素 numbers 是一个 Array。当你没有使用数组实用程序打印它时,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 swimming
A fish is swimming

6. 总结

在运算符、列表和循环等基本方面,Kotlin 与其他语言非常相似,但有一些重要的区别。

以下特性在 Kotlin 中可能与你在其他语言中习惯的不同

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

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

  • 数组和列表可以具有单一类型或混合类型。
  • 数组和列表可以嵌套。
  • 你可以使用 forwhiledo/whilerepeat 创建循环。
  • 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:欢迎来到课程。”