练习:类和集合

1. 开始之前

在本学习路径中,您学习了泛型、不同类型的类、集合和高阶函数。为了练习您学到的知识,您将帮助您的团队改进他们新的事件跟踪应用程序。每个步骤的说明都描述了应用程序的当前状态以及您需要完成的任务。

建议您使用 Kotlin Playground 来完成这些练习。

先决条件

  • 完成使用 Compose 的 Android 基础知识课程的单元 3 学习路径 1及其之前的学习路径。
  • 熟悉 Kotlin 编程语言的基础知识,包括类、对象、集合和高阶函数。

您需要什么

2. 应用程序概述

您是事件跟踪应用程序团队最新的软件工程师。此应用程序的目的是允许用户跟踪他们的事件。您的团队将分配任务给您,以帮助构建应用程序的功能。

在每个任务结束时,您应该将您的解决方案与提供的解决方案进行比较。实现所需功能的方法有很多种,因此如果您的代码与提供的解决方案代码不完全匹配,请不要担心。

使用上一个任务中提供的解决方案代码作为下一个任务的起始代码,以便从一个共同的起点开始。

3. 任务 1

另一位软件工程师已经完成了应用程序的一些高级工作,而您的任务是实现细节。

您需要实现Event类。此类用于保存用户输入的事件的详细信息。(提示:此类不需要定义任何方法或执行任何操作。)

对于此任务,您需要创建一个名为Event的数据类。

此类的实例应该能够存储

  • 事件标题(字符串)。
  • 事件描述(字符串,可以为空)。
  • 事件时间段(字符串)。我们只需要跟踪事件是在上午、下午还是晚上开始。
  • 事件持续时间(以分钟为单位的整数)。

在继续之前,请尝试自己编写代码。

使用您的代码,使用以下信息创建一个实例

  • 标题:学习 Kotlin
  • 描述:每天至少学习 Kotlin 15 分钟。
  • 时间段:晚上
  • 持续时间: 15

尝试打印您的对象以验证您是否获得以下输出

Event(title=Study Kotlin, description=Commit to studying Kotlin at least 15 minutes per day., daypart=Evening, durationInMinutes=15)

完成任务后,或尽力尝试后,点击下一步查看我们的代码。

4. 任务 1 解决方案

您的解决方案应该类似于以下代码

data class Event(
    val title: String,
    val description: String? = null,
    val daypart: String,
    val durationInMinutes: Int,
)

5. 任务 2

为了让项目按计划进行,您的经理决定使用我们为数据类提供的代码。

您的团队成员使用Event类一段时间后,高级队友意识到使用字符串表示时间段并不理想。

一些开发人员存储了“Morning”的值,一些使用了“morning”,还有一些使用了“MORNING”。

这导致了许多问题。

您的任务是通过进行一些重构来解决此问题。重构是在不改变其功能的情况下改进代码的过程。一些示例包括简化逻辑或将重复的代码移动到单独的函数中。

可以使用哪种类型的类来建模一组有限的 distinct 值以帮助纠正此问题?

您的团队希望您更改 daypart 代码以使用枚举类。通过使用枚举类,您的队友被迫选择提供的 daypart 值之一,这可以防止此类问题。

枚举类应该命名为Daypart。它应该有三个值

  • MORNING
  • AFTERNOON
  • EVENING

您将如何创建此枚举类?

您将如何重构您的Event类以使用它?

现在尝试编写您的解决方案代码,然后再继续。

点击下一步查看我们的代码。

6. 任务 2 解决方案

enum class Daypart {
    MORNING,
    AFTERNOON,
    EVENING,
}

重构后的Event数据类现在使用枚举类

data class Event(
    val title: String,
    val description: String? = null,
    val daypart: Daypart,
    val durationInMinutes: Int,
)

7. 任务 3

您的同事喜欢使用重构后的Daypart,但他们还有其他问题。

以下代码是他们目前创建和存储用户事件的方式。

val event1 = Event(title = "Wake up", description = "Time to get up", daypart = Daypart.MORNING, durationInMinutes = 0)
val event2 = Event(title = "Eat breakfast", daypart = Daypart.MORNING, durationInMinutes = 15)
val event3 = Event(title = "Learn about Kotlin", daypart = Daypart.AFTERNOON, durationInMinutes = 30)
val event4 = Event(title = "Practice Compose", daypart = Daypart.AFTERNOON, durationInMinutes = 60)
val event5 = Event(title = "Watch latest DevBytes video", daypart = Daypart.AFTERNOON, durationInMinutes = 10)
val event6 = Event(title = "Check out latest Android Jetpack library", daypart = Daypart.EVENING, durationInMinutes = 45)

他们创建了许多事件,每个事件目前都需要它自己的变量。随着创建更多事件,跟踪所有事件变得越来越困难。使用这种方法,确定用户安排了多少事件有多难?

您可以想到更好的方法来组织这些事件的存储吗?

您可以将所有事件存储在一个变量中的什么方法?(注意:它必须灵活,因为可能会添加更多事件。它还需要有效地返回存储在变量中的事件数量。)

您将使用哪个类或数据类型?添加更多事件的一种方法是什么?

现在轮到您来实现此功能了。尝试编写代码,然后再点击下一步查看我们的解决方案。

8. 任务 3 解决方案

val events = mutableListOf<Event>(event1, event2, event3, event4, event5, event6)

9. 任务 4

您的经理喜欢应用程序的进展情况,但决定用户应该能够根据事件的持续时间查看其事件的摘要。例如,“您有 5 个短事件”。

“短”事件是指持续时间少于 60 分钟的事件。

使用上一个任务解决方案中的events变量代码,您将如何实现此结果?

点击下一步继续我们的解决方案。

10. 任务 4 解决方案

有多种方法可以实现这一点,以下是我们决定使用的方法

val shortEvents = events.filter { it.durationInMinutes < 60 }
println("You have ${shortEvents.size} short events.")

11. 任务 5

您的队友喜欢应用程序的进展情况,但他们希望用户能够查看所有事件及其时间段的摘要。

输出应该类似于

Morning: 3 events
Afternoon: 4 events
Evening: 2 events

使用上一步的events变量代码,您如何实现此结果?

点击下一步查看解决方案代码。

12. 任务 5 解决方案

以下是我们的解决方案,但其他变体也可以接受。

val groupedEvents = events.groupBy { it.daypart }
groupedEvents.forEach { (daypart, events) ->
    println("$daypart: ${events.size} events")
}

13. 任务 6

目前,您的同事使用其索引查找并打印最后一项。使用的代码是:println("Last event of the day: ${events[events.size - 1].title}")

您的经理建议检查Kotlin 文档中可以简化此代码的函数。

您找到了哪个函数?

尝试使用它来确认您是否获得了相同的打印结果。

点击下一步查看解决方案。

14. 任务 6 解决方案

println("Last event of the day: ${events.last().title}")

15. 任务 7

您的团队喜欢您设计的 data class,但发现每次需要事件持续时间的字符串时编写代码都比较重复

val durationOfEvent = if (events[0].durationInMinutes < 60) {
        "short"
    } else {
        "long"
    }
println("Duration of first event of the day: $durationOfEvent")

虽然您可以通过直接向类中添加方法来修复此重复,但这并不理想,因为其他团队也开始在他们的应用程序中使用您的事件类。如果类发生更改,他们将需要重新测试所有代码以确保没有因为您的更改而中断。

无需直接更改数据类,您如何编写一个扩展属性来返回与上述代码相同的 value?

正确实现后,您可以使用以下代码,它将打印与本任务开头显示的代码相同的 message。

println("Duration of first event of the day: ${events[0].durationOfEvent}")

点击下一步继续到解决方案。

16. 任务 7 解决方案

val Event.durationOfEvent: String
    get() = if (this.durationInMinutes < 60) {
        "short"
    } else {
        "long"
    }

17. 额外练习

有关 Kotlin 语言的更多练习,请查看JetBrains Academy 的 Kotlin Core 学习路径。要跳转到特定主题,请转到知识图谱以查看学习路径中涵盖的主题列表。