1. 开始之前
既然您已经努力学习了 Kotlin 编程的基础知识,现在是时候将所学知识付诸实践了。
这些练习测试您对所学概念的理解。它们基于现实世界的用例,其中一些您可能以前作为用户遇到过。
按照说明在 Kotlin Playground 中为每个练习找到解决方案。如果您遇到困难,一些练习有提示可以帮助您。每个练习的解决方案代码在最后提供,但建议您在检查答案之前先解决这些练习。
以您舒适的速度完成练习。练习有持续时间估计,但它们只是估计,因此您不必遵守它们。花尽可能多的时间仔细解决每个问题。解决方案只是解决练习的一种方法,因此您可以随意尝试任何您感到舒适的方式。
先决条件
- 熟悉 Kotlin Playground
- 能够定义和调用函数。
- 了解 Kotlin 编程基础知识,包括变量以及
println()
和main()
函数。 - 熟悉 Kotlin 条件语句,包括
if/else
和when
语句和表达式 - 熟悉 Kotlin lambda 表达式
- 了解如何处理可空变量。
- 了解如何创建 Kotlin 类和对象。
- 已完成 在 Kotlin 中编写条件语句、在 Kotlin 中使用可空性、在 Kotlin 中使用类和对象 和 在 Kotlin 中使用函数类型和 lambda 表达式 代码实验室。
您需要什么
- Kotlin Playground
2. 移动通知
通常,您的手机会为您提供通知摘要。
在以下代码片段中提供的初始代码中,编写一个程序,根据您收到的通知数量打印摘要消息。消息应包括
- 当通知少于 100 个时,显示确切的通知数量。
- 当通知数量为 100 个或更多时,显示
99+
作为通知数量。
fun main() {
val morningNotification = 51
val eveningNotification = 135
printNotificationSummary(morningNotification)
printNotificationSummary(eveningNotification)
}
fun printNotificationSummary(numberOfMessages: Int) {
// Fill in the code.
}
完成 printNotificationSummary()
函数,以便程序打印以下行
You have 51 notifications. Your phone is blowing up! You have 99+ notifications.
3. 电影票价格
电影票通常根据观影者的年龄以不同的价格定价。
在以下代码片段中提供的初始代码中,编写一个程序来计算这些基于年龄的票价
- 12 岁或以下儿童票价为 15 美元。
- 13 至 60 岁的标准票价为 30 美元。在星期一,将同一年龄段的标准票价折扣至 25 美元。
- 61 岁及以上老年人票价为 20 美元。假设观影者的最大年龄为 100 岁。
- 当用户输入的年龄超出年龄规范时,使用
-1
值表示价格无效。
fun main() {
val child = 5
val adult = 28
val senior = 87
val isMonday = true
println("The movie ticket price for a person aged $child is \$${ticketPrice(child, isMonday)}.")
println("The movie ticket price for a person aged $adult is \$${ticketPrice(adult, isMonday)}.")
println("The movie ticket price for a person aged $senior is \$${ticketPrice(senior, isMonday)}.")
}
fun ticketPrice(age: Int, isMonday: Boolean): Int {
// Fill in the code.
}
完成 ticketPrice()
函数,以便程序打印以下行
The movie ticket price for a person aged 5 is $15. The movie ticket price for a person aged 28 is $25. The movie ticket price for a person aged 87 is $20.
4. 温度转换器
世界上使用三种主要的温度刻度:摄氏度、华氏度和开尔文。
在以下代码片段中提供的初始代码中,编写一个程序,使用以下公式将温度从一个刻度转换为另一个刻度
- 摄氏度转换为华氏度:° F = 9/5 (° C) + 32
- 开尔文转换为摄氏度:° C = K - 273.15
- 华氏度转换为开尔文:K = 5/9 (° F - 32) + 273.15
请注意,String.format("%.2f", /* measurement */ )
方法用于将数字转换为带有 2 位小数的 String
类型。
fun main() {
// Fill in the code.
}
fun printFinalTemperature(
initialMeasurement: Double,
initialUnit: String,
finalUnit: String,
conversionFormula: (Double) -> Double
) {
val finalMeasurement = String.format("%.2f", conversionFormula(initialMeasurement)) // two decimal places
println("$initialMeasurement degrees $initialUnit is $finalMeasurement degrees $finalUnit.")
}
完成 main()
函数,以便它调用 printFinalTemperature()
函数并打印以下行。您需要为温度和转换公式传递参数。提示:您可能希望使用 Double
值来避免在除法运算期间出现 Integer
截断。
27.0 degrees Celsius is 80.60 degrees Fahrenheit. 350.0 degrees Kelvin is 76.85 degrees Celsius. 10.0 degrees Fahrenheit is 260.93 degrees Kelvin.
5. 歌曲目录
假设您需要创建一个音乐播放器应用程序。
创建一个可以表示歌曲结构的类。 Song
类必须包含以下代码元素
- 标题、艺术家、发布年份和播放次数的属性
- 一个属性,指示歌曲是否流行。如果播放次数少于 1,000 次,则认为它不流行。
- 一个方法,以以下格式打印歌曲描述
"[标题],由 [艺术家] 演唱,于 [发布年份] 发布。"
6. 互联网个人资料
通常,您需要填写在线网站的个人资料,其中包含必填字段和非必填字段。例如,您可以添加您的个人信息并链接到推荐您注册个人资料的其他人员。
在以下代码片段中提供的初始代码中,编写一个程序打印出某个人的个人资料详细信息。
fun main() {
val amanda = Person("Amanda", 33, "play tennis", null)
val atiqah = Person("Atiqah", 28, "climb", amanda)
amanda.showProfile()
atiqah.showProfile()
}
class Person(val name: String, val age: Int, val hobby: String?, val referrer: Person?) {
fun showProfile() {
// Fill in code
}
}
完成 showProfile()
函数,以便程序打印以下行
Name: Amanda Age: 33 Likes to play tennis. Doesn't have a referrer. Name: Atiqah Age: 28 Likes to climb. Has a referrer named Amanda, who likes to play tennis.
7. 可折叠手机
通常,当按下电源按钮时,手机屏幕会打开和关闭。相反,如果可折叠手机折叠起来,则按下电源按钮时,可折叠手机上的主要内屏不会打开。
在以下代码片段中提供的初始代码中,编写一个 FoldablePhone
类,该类继承自 Phone
类。它应该包含以下内容
- 一个属性,指示手机是否折叠。
- 与
Phone
类不同的switchOn()
函数行为,以便仅在手机未折叠时打开屏幕。 - 更改折叠状态的方法。
class Phone(var isScreenLightOn: Boolean = false){
fun switchOn() {
isScreenLightOn = true
}
fun switchOff() {
isScreenLightOn = false
}
fun checkPhoneScreenLight() {
val phoneScreenLight = if (isScreenLightOn) "on" else "off"
println("The phone screen's light is $phoneScreenLight.")
}
}
8. 特别拍卖
通常在拍卖中,最高出价者决定物品的价格。在这个特殊的拍卖中,如果某个物品没有竞拍者,则该物品将自动以最低价格出售给拍卖行。
在以下代码片段中提供的初始代码中,您将获得一个 auctionPrice()
函数,该函数接受一个可为空的 Bid?
类型作为参数
fun main() {
val winningBid = Bid(5000, "Private Collector")
println("Item A is sold at ${auctionPrice(winningBid, 2000)}.")
println("Item B is sold at ${auctionPrice(null, 3000)}.")
}
class Bid(val amount: Int, val bidder: String)
fun auctionPrice(bid: Bid?, minimumPrice: Int): Int {
// Fill in the code.
}
完成 auctionPrice()
函数,以便程序打印以下行
Item A is sold at 5000. Item B is sold at 3000.
9. 解决方案代码
移动通知
该解决方案使用 if/else
语句根据收到的通知消息数量打印相应的通知摘要消息
fun main() {
val morningNotification = 51
val eveningNotification = 135
printNotificationSummary(morningNotification)
printNotificationSummary(eveningNotification)
}
fun printNotificationSummary(numberOfMessages: Int) {
if (numberOfMessages < 100) {
println("You have ${numberOfMessages} notifications.")
} else {
println("Your phone is blowing up! You have 99+ notifications.")
}
}
电影票价格
该解决方案使用 when
表达式根据观影者的年龄返回相应的票价。它还为其中一个 when
表达式的分支使用简单的 if/else
表达式来添加标准票价的附加条件。
else
分支中的票价返回 -1
值,这表示为 else
分支设置的价格无效。更好的实现是让 else
分支抛出异常。您将在以后的单元中学习异常处理。
fun main() {
val child = 5
val adult = 28
val senior = 87
val isMonday = true
println("The movie ticket price for a person aged $child is \$${ticketPrice(child, isMonday)}.")
println("The movie ticket price for a person aged $adult is \$${ticketPrice(adult, isMonday)}.")
println("The movie ticket price for a person aged $senior is \$${ticketPrice(senior, isMonday)}.")
}
fun ticketPrice(age: Int, isMonday: Boolean): Int {
return when(age) {
in 0..12 -> 15
in 13..60 -> if (isMonday) 25 else 30
in 61..100 -> 20
else -> -1
}
}
温度转换器
该解决方案要求您将函数作为参数传递给 printFinalTemperature()
函数。最简洁的解决方案将 lambda 表达式作为参数传递,使用 it
参数引用代替参数名称,并使用尾随 lambda 语法。
fun main() {
printFinalTemperature(27.0, "Celsius", "Fahrenheit") { 9.0 / 5.0 * it + 32 }
printFinalTemperature(350.0, "Kelvin", "Celsius") { it - 273.15 }
printFinalTemperature(10.0, "Fahrenheit", "Kelvin") { 5.0 / 9.0 * (it - 32) + 273.15 }
}
fun printFinalTemperature(
initialMeasurement: Double,
initialUnit: String,
finalUnit: String,
conversionFormula: (Double) -> Double
) {
val finalMeasurement = String.format("%.2f", conversionFormula(initialMeasurement)) // two decimal places
println("$initialMeasurement degrees $initialUnit is $finalMeasurement degrees $finalUnit.")
}
歌曲目录
该解决方案包含一个 Song
类,该类具有一个默认构造函数,它接受所有必需的参数。 Song
类还具有一个 isPopular
属性,该属性使用自定义 getter 函数,以及一个打印其自身描述的方法。您可以在 main()
函数中创建类的实例并调用其方法来测试实现是否正确。您可以在编写大数字(例如 1_000_000
值)时使用下划线以使其更易读。
fun main() {
val brunoSong = Song("We Don't Talk About Bruno", "Encanto Cast", 2022, 1_000_000)
brunoSong.printDescription()
println(brunoSong.isPopular)
}
class Song(
val title: String,
val artist: String,
val yearPublished: Int,
val playCount: Int
){
val isPopular: Boolean
get() = playCount >= 1000
fun printDescription() {
println("$title, performed by $artist, was released in $yearPublished.")
}
}
当您在实例的方法上调用 println()
函数时,程序可能会打印以下输出
We Don't Talk About Bruno, performed by Encanto Cast, was released in 2022. true
互联网个人资料
该解决方案在各种 if/else
语句中包含空检查,以根据各种类属性是否为 null
打印不同的文本
fun main() {
val amanda = Person("Amanda", 33, "play tennis", null)
val atiqah = Person("Atiqah", 28, "climb", amanda)
amanda.showProfile()
atiqah.showProfile()
}
class Person(val name: String, val age: Int, val hobby: String?, val referrer: Person?) {
fun showProfile() {
println("Name: $name")
println("Age: $age")
if(hobby != null) {
print("Likes to $hobby. ")
}
if(referrer != null) {
print("Has a referrer named ${referrer.name}")
if(referrer.hobby != null) {
print(", who likes to ${referrer.hobby}.")
} else {
print(".")
}
} else {
print("Doesn't have a referrer.")
}
print("\n\n")
}
}
可折叠手机
为了使 Phone
类成为父类,您需要在类名前添加 open
关键字使该类开放。要覆盖 FoldablePhone
类中的 switchOn()
方法,您需要在 Phone
类中的方法前添加 open
关键字使该方法开放。
该解决方案包含一个 FoldablePhone
类,该类具有一个默认构造函数,其中包含 isFolded
参数的默认参数。 FoldablePhone
类还具有两个方法来将 isFolded
属性更改为 true
或 false
值。它还覆盖了从 Phone
类继承的 switchOn()
方法。
您可以在 main()
函数中创建类的实例并调用其方法来测试实现是否正确。
open class Phone(var isScreenLightOn: Boolean = false){
open fun switchOn() {
isScreenLightOn = true
}
fun switchOff() {
isScreenLightOn = false
}
fun checkPhoneScreenLight() {
val phoneScreenLight = if (isScreenLightOn) "on" else "off"
println("The phone screen's light is $phoneScreenLight.")
}
}
class FoldablePhone(var isFolded: Boolean = true): Phone() {
override fun switchOn() {
if (!isFolded) {
isScreenLightOn = true
}
}
fun fold() {
isFolded = true
}
fun unfold() {
isFolded = false
}
}
fun main() {
val newFoldablePhone = FoldablePhone()
newFoldablePhone.switchOn()
newFoldablePhone.checkPhoneScreenLight()
newFoldablePhone.unfold()
newFoldablePhone.switchOn()
newFoldablePhone.checkPhoneScreenLight()
}
输出如下
The phone screen's light is off. The phone screen's light is on.
特别拍卖
该解决方案使用 ?.
安全调用运算符和 ?:
Elvis 运算符返回正确价格
fun main() {
val winningBid = Bid(5000, "Private Collector")
println("Item A is sold at ${auctionPrice(winningBid, 2000)}.")
println("Item B is sold at ${auctionPrice(null, 3000)}.")
}
class Bid(val amount: Int, val bidder: String)
fun auctionPrice(bid: Bid?, minimumPrice: Int): Int {
return bid?.amount ?: minimumPrice
}
10. 额外练习
有关 Kotlin 语言的更多练习,请查看 JetBrains Academy 的 Kotlin 核心课程。要跳转到特定主题,请转到 知识图谱 以查看课程中涵盖的主题列表。