程序员 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 不会在数字类型之间隐式转换,因此您不能直接将短整型值赋给长整型变量,或将 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 中的字符串与任何其他使用 " 表示字符串和 ' 表示单个字符的编程语言中的字符串几乎一样,您可以使用 + 运算符连接字符串。您可以通过将字符串与值组合来创建字符串模板;$variable 名称将替换为表示该值的文本。这称为 *变量插值*。

  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. 编写一个包含多个 case 的 if。对于更复杂的条件,使用逻辑与 && 和逻辑或 ||。与其他语言一样,您可以使用 else if 来拥有多个 case。
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

IntelliJ IDEA 的文档可在 JetBrains 网站上找到。

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:欢迎来到课程”。