在 Kotlin 中创建和使用函数

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。您可以简单地复制粘贴代码,也可以为生日问候创建一个单独的函数。您将选择后者。为特定任务创建单独的函数有许多好处。

  • 可重用代码:无需复制粘贴需要多次使用的代码,您只需在需要时调用函数即可。
  • 可读性:确保函数只执行一个特定任务有助于其他开发者和队友,以及您自己将来准确了解一段代码的作用。

定义函数的语法如下图所示。

Syntax for defining a function

函数定义以 fun 关键字开头,后跟函数名称、一对开括号和闭括号,以及一对开花括号和闭花括号。花括号中包含的是函数被调用时将运行的代码。

您将创建一个新函数,将两个 println() 语句移出 main() 函数。

  1. 在您的浏览器中,打开 Kotlin Playground,并将内容替换为以下代码。
fun main() {
    println("Happy Birthday, Rover!")
    println("You are now 5 years old!")
}
  1. main() 函数之后,定义一个名为 birthdayGreeting() 的新函数。此函数使用与 main() 函数相同的语法声明。
fun main() {
    println("Happy Birthday, Rover!")
    println("You are now 5 years old!")
}

fun birthdayGreeting() {
    
}
  1. 将两个 println() 语句从 main() 移动到 birthdayGreeting() 函数的花括号内。
fun main() {
    
}

fun birthdayGreeting() {
    println("Happy Birthday, Rover!")
    println("You are now 5 years old!")
}
  1. main() 函数中,调用 birthdayGreeting() 函数。您的最终代码应如下所示
fun main() {
    birthdayGreeting()
}

fun birthdayGreeting() {
    println("Happy Birthday, Rover!")
    println("You are now 5 years old!")
}
  1. 运行您的代码。您应该看到以下输出
Happy Birthday, Rover!
You are now 5 years old!

3. 从函数返回一个值

在更复杂的应用中,函数不仅仅是打印文本。

Kotlin 函数还可以生成称为*返回值*的数据,该数据存储在变量中,您可以在代码的其他地方使用它。

定义函数时,您可以指定希望它返回的值的数据类型。返回类型通过在括号后放置冒号 (:)、一个空格,然后是类型名称 (IntString 等) 来指定。返回类型和开花括号之间会放置一个空格。在函数体内部,所有语句之后,您可以使用 return 语句来指定您希望函数返回的值。return 语句由 return 关键字后跟您希望函数作为输出返回的值(例如变量)组成。

声明具有返回类型的函数的语法如下。

Syntax for declaring a function with a return type

Unit 类型

默认情况下,如果您未指定返回类型,则默认返回类型为 UnitUnit 表示函数不返回值。Unit 等效于其他语言中的 void 返回类型(Java 和 C 中的 void;Swift 中的 Void/空元组 ();Python 中的 None 等)。任何不返回值的函数都会隐式返回 Unit。您可以通过修改代码使其返回 Unit 来观察这一点。

  1. birthdayGreeting() 的函数声明中,在闭括号后添加冒号,并将返回类型指定为 Unit
fun main() {
    birthdayGreeting()
}

fun birthdayGreeting(): Unit {
    println("Happy Birthday, Rover!")
    println("You are now 5 years old!")
}
  1. 运行代码,观察一切仍然正常工作。
Happy Birthday, Rover!
You are now 5 years old!

在 Kotlin 中指定 Unit 返回类型是可选的。对于不返回任何内容或返回 Unit 的函数,您不需要 return 语句。

birthdayGreeting() 返回一个 String

为了演示函数如何返回值,您将修改 birthdayGreeting() 函数以返回一个字符串,而不是简单地打印结果。

  1. Unit 返回类型替换为 String
fun birthdayGreeting(): String {
    println("Happy Birthday, Rover!")
    println("You are now 5 years old!")
}
  1. 运行您的代码。您会得到一个错误。如果您为函数声明了一个返回类型(例如 String),那么该函数必须包含一个 return 语句。
A 'return' expression required in a function with a block body ('{...}')
  1. 您只能从函数返回一个字符串,而不是两个。使用 val 关键字将 println() 语句替换为两个变量:nameGreetingageGreeting。因为您从 birthdayGreeting() 中移除了对 println() 的调用,所以调用 birthdayGreeting() 不会打印任何内容。
fun birthdayGreeting(): String {
    val nameGreeting = "Happy Birthday, Rover!"
    val ageGreeting = "You are now 5 years old!"
}
  1. 使用您在早期 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"
}
  1. main() 中,由于 birthdayGreeting() 返回一个值,您可以将结果存储在一个字符串变量中。使用 val 声明一个 greeting 变量来存储调用 birthdayGreeting() 的结果。
fun main() {
    val greeting = birthdayGreeting()
}
  1. main() 中,调用 println() 来打印 greeting 字符串。main() 函数现在应如下所示。
fun main() {
    val greeting = birthdayGreeting()
    println(greeting)
}
  1. 运行您的代码,然后观察结果与之前相同。返回值允许您将结果存储在变量中,但是如果您在 println() 函数内部调用 birthdayGreeting() 函数,您认为会发生什么?
Happy Birthday, Rover!
You are now 5 years old!
  1. 移除变量,然后将调用 birthdayGreeting() 函数的结果传递给 println() 函数
fun main() {
    println(birthdayGreeting())
}
  1. 运行您的代码并观察输出。调用 birthdayGreeting() 的返回值直接传递给 println()
Happy Birthday, Rover!
You are now 5 years old!

4. 为 birthdayGreeting() 函数添加参数

如您所见,当您调用 println() 时,可以在括号内包含一个字符串或*将值传递*给函数。您也可以对 birthdayGreeting() 函数执行相同的操作。但是,您首先需要为 birthdayGreeting() 添加一个*参数*。

参数指定一个变量名和数据类型,您可以将其作为数据传递给函数,以便在函数内部访问。参数在函数名后的括号内声明。

Syntax for declaring a function with parameters and a return type

每个参数由一个变量名和数据类型组成,之间用冒号和空格分隔。多个参数之间用逗号分隔。

目前,birthdayGreeting() 函数只能用于问候 Rover。您将向 birthdayGreeting() 函数添加一个参数,以便您可以问候传递给函数的任何名称。

  1. birthdayGreeting() 函数的括号内,使用语法 name: String 添加一个类型为 Stringname 参数。
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 中,您学习了如何将变量的值插入到字符串中。

  1. 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"
}
  1. 运行您的代码并观察错误。现在您已经声明了 name 参数,在调用 birthdayGreeting() 时需要传入一个 String。当您调用接受参数的函数时,您会向函数传递一个实参。实参是您传递的值,例如 "Rover"
No value passed for parameter 'name'
  1. "Rover" 传入 main() 中的 birthdayGreeting() 调用。
fun main() {
    println(birthdayGreeting("Rover"))
}
  1. 运行您的代码并观察输出。名称 Rover 来自 name 参数。
Happy Birthday, Rover!
You are now 5 years old!
  1. 由于 birthdayGreeting() 接受一个参数,因此您可以使用 Rover 以外的名称来调用它。在对 println() 的调用内部,再添加一个对 birthdayGreeting() 的调用,传入实参 "Rex"
println(birthdayGreeting("Rover"))
println(birthdayGreeting("Rex"))
  1. 再次运行代码,然后观察输出会因传递给 birthdayGreeting() 的实参而异。
Happy Birthday, Rover!
You are now 5 years old!
Happy Birthday, Rex!
You are now 5 years old!

5. 具有多个参数的函数

之前,您添加了一个参数以根据名称更改问候语。但是,您还可以为一个函数定义多个参数,甚至是不同数据类型的参数。在本节中,您将修改问候语,使其也根据狗的年龄而变化。

Function with multiple parameters

参数定义用逗号分隔。同样,当您调用具有多个参数的函数时,传入的实参也用逗号分隔。让我们看看实际操作。

  1. name 参数之后,向 birthdayGreeting() 函数添加一个类型为 Intage 参数。新的函数声明应包含两个参数 nameage,并用逗号分隔
fun birthdayGreeting(name: String, age: Int): String {
    val nameGreeting = "Happy Birthday, $name!"
    val ageGreeting = "You are now 5 years old!"
    return "$nameGreeting\n$ageGreeting"
}
  1. 新的问候字符串应使用 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"
}
  1. 运行函数,然后注意输出中的错误
No value passed for parameter 'age'
No value passed for parameter 'age'
  1. 修改 main() 中对 birthdayGreeting() 函数的两次调用,为每只狗传入不同的年龄。为 Rover 的年龄传入 5,为 Rex 的年龄传入 2
fun main() {
    println(birthdayGreeting("Rover", 5))
    println(birthdayGreeting("Rex", 2))
}
  1. 运行您的代码。现在您已为两个参数传入了值,当您调用函数时,输出应反映每只狗的名称和年龄。
Happy Birthday, Rover!
You are now 5 years old!
Happy Birthday, Rex!
You are now 2 years old!

函数签名

到目前为止,您已经了解了如何定义函数名称、输入(参数)和输出。函数名称及其输入(参数)统称为*函数签名*。函数签名由返回类型之前的所有内容组成,并显示在以下代码片段中。

fun birthdayGreeting(name: String, age: Int)

参数(用逗号分隔)有时也称为参数列表。

您经常会在其他开发者编写的代码文档中看到这些术语。函数签名告诉您函数的名称以及可以传入的数据类型。

您已经学习了许多关于定义函数的新语法。请查看下图以回顾函数语法。

Diagram for a recap of function syntax

6. 命名参数

在前面的示例中,当您调用函数时,无需指定参数名称 nameage。但是,如果您选择这样做,也是可以的。例如,您可能调用一个包含许多参数的函数,或者您可能希望以不同的顺序传递实参,例如将 age 参数放在 name 参数之前。当您在调用函数时包含参数名称,这称为命名参数。尝试将命名参数与 birthdayGreeting() 函数一起使用。

  1. 修改对 Rex 的调用,使其使用此代码片段中所示的命名参数。您可以通过包含参数名称后跟等号,然后是值来完成此操作(例如 name = "Rex")。
println(birthdayGreeting(name = "Rex", age = 2))
  1. 运行代码,然后观察输出未改变
Happy Birthday, Rover!
You are now 5 years old!
Happy Birthday, Rex!
You are now 2 years old!
  1. 重新排列命名参数。例如,将 age 命名参数放在 name 命名参数之前。
println(birthdayGreeting(age = 2, name = "Rex"))
  1. 运行代码并观察输出保持不变。即使您更改了实参的顺序,相同的参数也会传入相同的值。
Happy Birthday, Rover!
You are now 5 years old!
Happy Birthday, Rex!
You are now 2 years old!

7. 默认参数

函数参数也可以指定默认参数。也许 Rover 是您最喜欢的狗,或者您希望在大多数情况下使用特定参数调用函数。当您调用函数时,您可以选择省略具有默认值的实参,在这种情况下,将使用默认值。

要添加默认参数,您可以在参数的数据类型后添加赋值运算符 (=) 并将其设置为一个值。修改您的代码以使用默认参数。

  1. birthdayGreeting() 函数中,将 name 参数设置为默认值 "Rover"
fun birthdayGreeting(name: String = "Rover", age: Int): String {
    return "Happy Birthday, $name! You are now $age years old!"
}
  1. main() 中对 Rover 的第一次 birthdayGreeting() 调用中,将 age 命名参数设置为 5。由于 age 参数在 name 之后定义,因此您需要使用命名参数 age。如果没有命名参数,Kotlin 会假定实参的顺序与参数的定义顺序相同。使用命名参数是为了确保 Kotlin 期望 age 参数是 Int 类型。
println(birthdayGreeting(age = 5))
println(birthdayGreeting("Rex", 2))
  1. 运行您的代码。对 birthdayGreeting() 函数的第一次调用将“Rover”打印为名称,因为您从未指定名称。对 birthdayGreeting() 的第二次调用仍然使用 Rex 值,这是您为 name 传入的。
Happy Birthday, Rover! You are now 5 years old!
Happy Birthday, Rex! You are now 2 years old!
  1. 从对 birthdayGreeting() 函数的第二次调用中删除名称。同样,由于省略了 name,您需要为年龄使用命名参数。
println(birthdayGreeting(age = 5))
println(birthdayGreeting(age = 2))
  1. 运行您的代码,然后观察现在对 birthdayGreeting() 的两次调用都将“Rover”打印为名称,因为没有传入名称实参。
Happy Birthday, Rover! You are now 5 years old!
Happy Birthday, Rover! You are now 2 years old!

8. 总结

恭喜!您学会了如何在 Kotlin 中定义和调用函数。

总结

  • 函数使用 fun 关键字定义,并包含可重用的代码片段。
  • 函数有助于使大型程序更易于维护,并防止代码不必要的重复。
  • 函数可以返回一个值,您可以将其存储在变量中以供以后使用。
  • 函数可以接受参数,参数是函数体内可用的变量。
  • 实参是您调用函数时传入的值。
  • 您可以在调用函数时命名实参。当您使用命名实参时,可以重新排列实参而不影响输出。
  • 您可以指定一个默认实参,允许您在调用函数时省略该实参。