练习:类和集合

1. 准备工作

在此开发者在线课程中,您学习了泛型、不同类型的类、集合以及高阶函数。为了练习所学的内容,您将帮助所在团队改进新的活动跟踪应用。每个步骤的说明都会描述应用的当前状态以及您应该完成的任务。

建议使用 Kotlin 园地来解答这些练习。

前提条件

  • 完成 Android 之 Compose 开发基础课程的第 3 单元在线课程 1 及其之前的在线课程。
  • 熟悉 Kotlin 编程语言的基础知识,包括类、对象、集合和高阶函数。

所需条件

  • 一台可连接互联网的计算机
  • 能够访问 Kotlin 园地

2. 应用概览

您是新加入活动跟踪应用团队的软件工程师。该应用旨在让用户能够跟踪他们的活动。您的任务是帮助构建应用的功能。

每个任务结束时,您应将您的解决方案与所提供的解决方案进行比较。实现所需功能的方式有很多种,因此如果您的代码与所提供的解决方案代码不完全一致,大可不必担心。

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

3. 任务 1

另一位软件工程师已经完成了该应用的一些主体工作,您将负责实现细节部分。

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

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

此类的实例应该能够存储:

  • 活动标题(以字符串表示)。
  • 活动说明(以字符串表示,可以为 null)。
  • 活动时段(以字符串表示)。我们只需跟踪活动在上午、下午还是晚上开始。
  • 活动时长(分钟,以整数表示)。

在继续操作之前,请尝试自行编写代码。

使用您的代码,根据以下信息创建实例:

  • 标题:学习 Kotlin
  • 说明:坚持每天至少学习 Kotlin 15 分钟。
  • 时段:晚上
  • 时长:15

请尝试输出对象以验证您是否获得了以下输出:

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

完成该任务或尽最大努力尝试该任务后,点击 Next 即可查看我们编写的代码。

4. 任务 1 解决方案

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

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

5. 任务 2

为确保项目顺利进行,您的经理决定使用我们为数据类提供的代码。

整个团队使用 Event 类一段时间后,资深的团队成员意识到使用字符串表示时段效果并不理想。

一些开发者存储了“Morning”值,还有一些使用了“morning”,另一些开发者使用了“MORNING”。

这会导致许多问题。

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

哪种类型的类可用于对一组有限的不同值进行建模,以帮助纠正此问题?

您的团队希望您将时段代码更改为使用枚举类。使用枚举类时,您的同事不得不选择所提供的时段值之一,这可以防止出现此类问题。

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

  • MORNING
  • AFTERNOON
  • EVENING

您将如何创建此枚举类?

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

请尝试立即编写解决方案代码,然后再继续。

点击 Next 即可查看我们编写的代码。

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)

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

您能想出更好的方法来整理这些活动的存储方式吗?

如何将所有活动存储在一个变量中?(注意:此方案必须灵活,因为可能会添加更多活动)。还需要能够高效地返回存储在变量中的活动数。)

您应使用哪种类或数据类型?可以通过哪种方法添加更多活动?

现在,您该实现此功能了。请先编写代码,然后点击 Next 查看我们的解决方案。

8. 任务 3 解决方案

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

9. 任务 4

您的经理很喜欢该应用目前的进展,但决定应该基于活动的时长向用户显示其短期活动的摘要。例如,“您有 5 个短期活动。”

“短期”活动是指不到 60 分钟的活动。

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

点击 Next 继续查看我们的解决方案。

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 变量代码,您如何实现此效果?

点击 Next 查看解决方案代码。

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 文档,寻找可以简化此代码的函数。

您发现了什么函数?

请尝试使用该函数来确认输出的结果是否相同。

点击 Next 查看解决方案。

14. 任务 6 解决方案

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

15. 任务 7

大家很喜欢您设计的数据类,但发现每次需要以字符串形式表示活动的时长时,都要重复编写代码:

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

虽然您可以通过直接向类添加方法来解决这种重复问题,但这并不理想,因为其他团队已开始在他们的应用中使用您的活动类。如果该类发生变化,他们需要重新测试所有代码,以确保您所做的更改不会造成任何中断。

如果不直接更改数据类,如何编写会返回与上述代码相同的值的扩展属性?

正确实现后,您将能够使用以下代码,它将输出与此任务开头所显示的代码相同的消息。

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

点击 Next 继续查看解决方案。

16. 任务 7 解决方案

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

17. 其他练习

如需尝试 Kotlin 语言的更多练习,请查看 JetBrains Academy 的 Kotlin Core 课程。如需跳转到特定主题,请前往知识图谱,查看上述课程涵盖的主题列表。