1. 开始之前
在之前的 Codelab 中,您看到了一个简单的程序,它打印了Hello, world!
。在您编写的程序中,您已经看到了两个函数
- 一个
main()
函数,每个 Kotlin 程序都需要此函数。它是程序的入口点或起点。 - 一个
println()
函数,您从main()
中调用它来输出文本。
在本 Codelab 中,您将了解有关函数的更多信息。
函数允许您将代码分解成可重用的部分,而不是将所有内容都包含在main()
中。函数是 Android 应用的基本构建块,学习如何定义和使用它们是您成为 Android 开发者的旅程中的重要一步。
先决条件
- Kotlin 编程基础知识,包括变量以及
println()
和main()
函数
您将学到什么
- 如何定义和调用您自己的函数。
- 如何从函数返回可以存储在变量中的值。
- 如何定义和调用具有多个参数的函数。
- 如何使用命名参数调用函数。
- 如何为函数参数设置默认值。
您需要什么
- 可以访问 Kotlin Playground 的网络浏览器
2. 定义和调用函数
在深入探讨函数之前,让我们回顾一些基本术语。
- 声明(或定义)函数使用
fun
关键字,并在花括号内包含代码,其中包含执行任务所需的指令。 - 调用函数会导致该函数中包含的所有代码都执行。
到目前为止,您已将所有代码都写入main()
函数中。main()
函数实际上并没有在您的代码中的任何地方被调用;Kotlin 编译器将其用作起点。main()
函数仅用于包含您想要执行的其他代码,例如对println()
函数的调用。
println()
函数是 Kotlin 语言的一部分。但是,您可以定义自己的函数。如果您需要多次调用它,这将允许重用您的代码。以下程序为例。
fun main() {
println("Happy Birthday, Rover!")
println("You are now 5 years old!")
}
main()
函数包含两个println()
语句,一个用于祝 Rover 生日快乐,另一个用于说明 Rover 的年龄。
虽然 Kotlin 允许您将所有代码都放在main()
函数中,但您可能并不总是希望这样做。例如,如果您还希望程序包含新年问候,则 main 函数也必须包含对println()
的这些调用。或者,也许您想多次问候 Rover。您可以简单地复制粘贴代码,也可以为生日问候创建单独的函数。您将执行后者。为特定任务创建单独的函数有很多好处。
- 可重用代码:与其复制粘贴您需要多次使用的代码,不如在需要的地方简单地调用函数。
- 可读性:确保函数只执行一项特定任务,这有助于其他开发人员和团队成员,以及未来的您准确地了解一段代码的作用。
定义函数的语法如下所示。
函数定义以fun
关键字开头,后跟函数名称、一组开括号和闭括号以及一组开花括号和闭花括号。花括号中包含调用函数时将运行的代码。
您将创建一个新函数,将两个println()
语句从main()
函数中移出。
- 在浏览器中,打开Kotlin Playground并将内容替换为以下代码。
fun main() {
println("Happy Birthday, Rover!")
println("You are now 5 years old!")
}
- 在
main()
函数之后,定义一个名为birthdayGreeting()
的新函数。此函数使用与main()
函数相同的语法声明。
fun main() {
println("Happy Birthday, Rover!")
println("You are now 5 years old!")
}
fun birthdayGreeting() {
}
- 将
main()
中的两个println()
语句移动到birthdayGreeting()
函数的花括号中。
fun main() {
}
fun birthdayGreeting() {
println("Happy Birthday, Rover!")
println("You are now 5 years old!")
}
- 在
main()
函数中,调用birthdayGreeting()
函数。完成后的代码应如下所示
fun main() {
birthdayGreeting()
}
fun birthdayGreeting() {
println("Happy Birthday, Rover!")
println("You are now 5 years old!")
}
- 运行您的代码。您应该看到以下输出
Happy Birthday, Rover! You are now 5 years old!
3. 从函数返回值
在更复杂的应用中,函数不仅仅打印文本。
Kotlin 函数还可以生成称为返回值的数据,该数据存储在变量中,您可以在代码的其他地方使用该变量。
在定义函数时,您可以指定要返回的值的数据类型。返回类型通过在括号后放置冒号(:
)、一个空格,然后是类型的名称(Int
、String
等)来指定。然后在返回类型和开花括号之间放置一个空格。在函数体中,在所有语句之后,您使用 return 语句指定要让函数返回的值。return 语句由return
关键字后跟要作为输出返回的值(例如变量)组成。
声明具有返回类型的函数的语法如下所示。
Unit
类型
默认情况下,如果您未指定返回类型,则默认返回类型为Unit
。Unit
表示函数不返回值。Unit
等效于其他语言中的 void 返回类型(Java 和 C 中的void
;Swift 中的Void
/空元组()
;Python 中的None
等)。任何不返回值的函数都隐式返回Unit
。您可以通过修改代码以返回Unit
来观察这一点。
- 在
birthdayGreeting()
的函数声明中,在闭括号后添加一个冒号,并将返回类型指定为Unit
。
fun main() {
birthdayGreeting()
}
fun birthdayGreeting(): Unit {
println("Happy Birthday, Rover!")
println("You are now 5 years old!")
}
- 运行代码并观察一切仍然有效。
Happy Birthday, Rover! You are now 5 years old!
在 Kotlin 中,指定Unit
返回类型是可选的。对于不返回任何内容或返回Unit
的函数,您不需要 return 语句。
从birthdayGreeting()
返回String
为了演示函数如何返回值,您将修改birthdayGreeting()
函数以返回字符串,而不是简单地打印结果。
- 将
Unit
返回类型替换为String
。
fun birthdayGreeting(): String {
println("Happy Birthday, Rover!")
println("You are now 5 years old!")
}
- 运行您的代码。您将收到错误。如果您为函数声明返回类型(例如
String
),则该函数必须包含return
语句。
A 'return' expression required in a function with a block body ('{...}')
- 您只能从一个函数返回一个字符串,而不是两个。使用
val
关键字将println()
语句替换为两个变量nameGreeting
和ageGreeting
。因为您已从birthdayGreeting()
中删除了对println()
的调用,所以调用birthdayGreeting()
不会打印任何内容。
fun birthdayGreeting(): String {
val nameGreeting = "Happy Birthday, Rover!"
val ageGreeting = "You are now 5 years old!"
}
- 使用您在之前的 Codelab 中学习的字符串格式化语法,添加一个
return
语句以从函数返回一个字符串,该字符串包含两个问候语。
为了在单独的行上格式化问候语,您还需要使用\n
转义字符。这就像您在之前的 Codelab 中学习的\"
转义字符一样。\n
字符将替换为换行符,以便两个问候语分别位于单独的行上。
fun birthdayGreeting(): String {
val nameGreeting = "Happy Birthday, Rover!"
val ageGreeting = "You are now 5 years old!"
return "$nameGreeting\n$ageGreeting"
}
- 在
main()
中,因为birthdayGreeting()
返回值,所以您可以将结果存储在字符串变量中。声明一个使用val
的greeting
变量以存储调用birthdayGreeting()
的结果。
fun main() {
val greeting = birthdayGreeting()
}
- 在
main()
中,调用println()
以打印greeting
字符串。main()
函数现在应如下所示。
fun main() {
val greeting = birthdayGreeting()
println(greeting)
}
- 运行您的代码,然后观察结果与之前相同。返回值允许您将结果存储在变量中,但是如果您在
println()
函数内调用birthdayGreeting()
函数,您认为会发生什么?
Happy Birthday, Rover! You are now 5 years old!
- 删除变量,然后将调用
birthdayGreeting()
函数的结果传递到println()
函数中
fun main() {
println(birthdayGreeting())
}
- 运行您的代码并观察输出。调用
birthdayGreeting()
的返回值直接传递到println()
中。
Happy Birthday, Rover! You are now 5 years old!
4. 向 birthdayGreeting() 函数添加参数
如您所见,当您调用println()
时,您可以在括号内包含字符串或将值传递给函数。您也可以对birthdayGreeting()
函数执行相同的操作。但是,您首先需要向birthdayGreeting()
添加一个参数。
参数指定变量的名称和数据类型,您可以将其作为数据传递到函数中,以便在函数内部访问。参数在函数名称后面的括号内声明。
每个参数都包含一个变量名称和数据类型,由冒号和空格分隔。多个参数用逗号分隔。
现在,birthdayGreeting()
函数只能用于问候 Rover。您将向birthdayGreeting()
函数添加一个参数,以便您可以问候传递到函数中的任何名称。
- 在
birthdayGreeting()
函数的括号内,添加一个类型为String
的name
参数,使用语法name: String
。
fun birthdayGreeting(name: String): String {
val nameGreeting = "Happy Birthday, Rover!"
val ageGreeting = "You are now 5 years old!"
return "$nameGreeting\n$ageGreeting"
}
上一步中定义的参数的工作方式类似于使用val
关键字声明的变量。其值可以在birthdayGreeting()
函数中的任何位置使用。在之前的 Codelab 中,您了解了如何将变量的值插入字符串中。
- 将
nameGreeting
字符串中的Rover
替换为$
符号,后跟name
参数。
fun birthdayGreeting(name: String): String {
val nameGreeting = "Happy Birthday, $name!"
val ageGreeting = "You are now 5 years old!"
return "$nameGreeting\n$ageGreeting"
}
- 运行代码并观察错误。既然您已经声明了
name
参数,那么在调用birthdayGreeting()
时,您需要传入一个String
。当您调用一个带有参数的函数时,您会向该函数传递一个参数。参数是您传递的值,例如"Rover"
。
No value passed for parameter 'name'
- 将
"Rover"
传入main()
中对birthdayGreeting()
的调用。
fun main() {
println(birthdayGreeting("Rover"))
}
- 运行代码并观察输出。名称 Rover 来自
name
参数。
Happy Birthday, Rover! You are now 5 years old!
- 因为
birthdayGreeting()
带有一个参数,所以您可以使用除 Rover 之外的其他名称来调用它。在对println()
的调用中,添加另一个对birthdayGreeting()
的调用,传入参数"Rex"
。
println(birthdayGreeting("Rover"))
println(birthdayGreeting("Rex"))
- 再次运行代码,然后观察输出根据传入
birthdayGreeting()
的参数的不同而不同。
Happy Birthday, Rover! You are now 5 years old! Happy Birthday, Rex! You are now 5 years old!
5. 带有多个参数的函数
之前,您添加了一个参数来根据名称更改问候语。但是,您还可以为函数定义多个参数,甚至不同数据类型的参数。在本节中,您将修改问候语,使其也根据狗的年龄而改变。
参数定义之间用逗号分隔。类似地,当您使用多个参数调用函数时,您也用逗号分隔传入的实参。让我们看看它是如何工作的。
- 在
name
参数之后,向birthdayGreeting()
函数添加一个类型为Int
的age
参数。新的函数声明应该包含两个参数,name
和age
,用逗号分隔。
fun birthdayGreeting(name: String, age: Int): String {
val nameGreeting = "Happy Birthday, $name!"
val ageGreeting = "You are now 5 years old!"
return "$nameGreeting\n$ageGreeting"
}
- 新的问候语字符串应该使用
age
参数。更新birthdayGreeting()
函数,以便在ageGreeting
字符串中使用age
参数的值。
fun birthdayGreeting(name: String, age: Int): String {
val nameGreeting = "Happy Birthday, $name!"
val ageGreeting = "You are now $age years old!"
return "$nameGreeting\n$ageGreeting"
}
- 运行函数,然后注意输出中的错误。
No value passed for parameter 'age' No value passed for parameter 'age'
- 修改
main()
中对birthdayGreeting()
函数的两次调用,以便为每只狗传入不同的年龄。为 Rover 的年龄传入5
,为 Rex 的年龄传入2
。
fun main() {
println(birthdayGreeting("Rover", 5))
println(birthdayGreeting("Rex", 2))
}
- 运行代码。现在您已经为这两个参数传入值,当您调用该函数时,输出应该反映每只狗的名称和年龄。
Happy Birthday, Rover! You are now 5 years old! Happy Birthday, Rex! You are now 2 years old!
函数签名
到目前为止,您已经了解了如何定义函数名称、输入(参数)和输出。函数名称及其输入(参数)统称为函数签名。函数签名包括返回类型之前的所有内容,如下面的代码片段所示。
fun birthdayGreeting(name: String, age: Int)
参数(用逗号分隔)有时称为参数列表。
您经常会在其他开发人员编写的代码的文档中看到这些术语。函数签名告诉您函数的名称以及可以传入什么数据类型。
您已经学习了许多关于定义函数的新语法。请查看以下图表,以回顾函数语法。
6. 具名参数
在前面的示例中,在调用函数时,您不需要指定参数名称 name
或 age
。但是,如果选择,您可以这样做。例如,您可能需要使用许多参数调用函数,或者您可能希望以不同的顺序传递您的实参,例如将 age
参数放在 name
参数之前。当您在调用函数时包含参数名称时,这称为具名参数。尝试对 birthdayGreeting()
函数使用具名参数。
- 修改 Rex 的调用以使用具名参数,如以下代码片段所示。您可以通过包含参数名称后跟等号,然后是值(例如
name = "Rex"
)来实现。
println(birthdayGreeting(name = "Rex", age = 2))
- 运行代码,然后观察输出未发生变化。
Happy Birthday, Rover! You are now 5 years old! Happy Birthday, Rex! You are now 2 years old!
- 重新排序具名参数。例如,将
age
具名参数放在name
具名参数之前。
println(birthdayGreeting(age = 2, name = "Rex"))
- 运行代码,观察输出保持不变。即使您更改了实参的顺序,也会为相同的参数传入相同的值。
Happy Birthday, Rover! You are now 5 years old! Happy Birthday, Rex! You are now 2 years old!
7. 默认参数
函数参数还可以指定默认参数。也许 Rover 是您最喜欢的狗,或者您期望函数在大多数情况下使用特定的参数被调用。当您调用函数时,可以选择省略具有默认值的实参,在这种情况下,将使用默认值。
要添加默认参数,您需要在参数的数据类型之后添加赋值运算符(=
),并将其设置为一个值。修改您的代码以使用默认参数。
- 在
birthdayGreeting()
函数中,将name
参数设置为默认值"Rover"
。
fun birthdayGreeting(name: String = "Rover", age: Int): String {
return "Happy Birthday, $name! You are now $age years old!"
}
- 在
main()
中对 Rover 的birthdayGreeting()
函数的第一次调用中,将age
具名参数设置为5
。因为age
参数是在name
之后定义的,所以您需要使用具名参数age
。如果没有具名参数,Kotlin 会假设实参的顺序与参数定义的顺序相同。具名参数用于确保 Kotlin 正在为age
参数期望一个Int
。
println(birthdayGreeting(age = 5))
println(birthdayGreeting("Rex", 2))
- 运行代码。对
birthdayGreeting()
函数的第一次调用打印 "Rover" 作为名称,因为您从未指定名称。对birthdayGreeting()
函数的第二次调用仍然使用您为name
传入的Rex
值。
Happy Birthday, Rover! You are now 5 years old! Happy Birthday, Rex! You are now 2 years old!
- 从对
birthdayGreeting()
函数的第二次调用中删除名称。同样,因为省略了name
,所以您需要为年龄使用具名参数。
println(birthdayGreeting(age = 5))
println(birthdayGreeting(age = 2))
- 运行代码,然后观察现在对
birthdayGreeting()
函数的两次调用都打印 "Rover" 作为名称,因为没有传入名称实参。
Happy Birthday, Rover! You are now 5 years old! Happy Birthday, Rover! You are now 2 years old!
8. 结论
恭喜!您已经学习了如何在 Kotlin 中定义和调用函数。
总结
- 函数用
fun
关键字定义,包含可重用的代码片段。 - 函数有助于使大型程序更易于维护,并防止不必要地重复代码。
- 函数可以返回值,您可以将该值存储在变量中以供以后使用。
- 函数可以接受参数,这些参数是在函数体内部可用的变量。
- 实参是在调用函数时传入的值。
- 您可以在调用函数时为实参命名。当您使用具名参数时,您可以重新排序实参而不影响输出。
- 您可以指定一个默认参数,使您在调用函数时可以省略该参数。