函数
你可以使用 fun
关键字在 Kotlin 中声明你自己的函数 。
fun hello() {
return println("Hello, world!")
}
fun main() {
hello()
// Hello, world!
}
在 Kotlin 中:
函数 参数写在圆括号 ()
内。
每个参数必须有一个类型,并且多个参数之间必须用逗号 ,
分隔。
返回类型写在函数的圆括号 ()
之后,用冒号 :
分隔。
函数的主体写在 花括号 {}
内。
return
关键字用于从函数中退出或返回某些内容。
在以下示例中:
x
和 y
是 函数 参数。
x
和 y
的类型是 Int
。
函数的返回类型是 Int
。
该函数在调用时返回 x
和 y
的和。
fun sum(x: Int, y: Int): Int {
return x + y
}
fun main() {
println(sum(1, 2))
// 3
}
具名参数 一般为了代码的简洁,你在调用函数时,并不用包含参数名称。 然而有时,包含参数名称确实会使你的代码更有可读性。这被称为使用 具名参数 。 如果你确实包含了参数名称,那么你可以按任意顺序编写参数。
fun printMessageWithPrefix(message: String, prefix: String) {
println("[$prefix] $message")
}
fun main() {
// 使用了具名参数,并交换了参数顺序
printMessageWithPrefix(prefix = "Log", message = "Hello")
// [Log] Hello
}
默认参数值 你可以为你的函数参数定义默认值。任何具有默认值的参数在调用你的函数时都可以省略。 要声明默认值,可以在类型后使用赋值运算符 =
:
fun printMessageWithPrefix(message: String, prefix: String = "Info") {
println("[$prefix] $message")
}
fun main() {
// 调用函数时传入了两个参数
printMessageWithPrefix("Hello", "Log")
// [Log] Hello
// 调用函数时只传入了 message 参数
printMessageWithPrefix("Hello")
// [Info] Hello
printMessageWithPrefix(prefix = "Log", message = "Hello")
// [Log] Hello
}
没有返回值的函数 如果你的函数没有返回有用的值,那么它的返回类型就是 Unit
。 Unit
是一个只有一个值的类型 ——Unit
。你不需要在函数体中显式声明返回 Unit
。 这意味着你不需要使用 return
关键字或声明返回类型:
fun printMessage(message: String) {
println(message)
// `return Unit` 或 `return` 是可选的
}
fun main() {
printMessage("Hello")
// Hello
}
单表达式函数 为了使代码更简洁,你可以使用单表达式函数。例如, sum()
函数可以这样简化:
fun sum(x: Int, y: Int): Int {
return x + y
}
fun main() {
println(sum(1, 2))
// 3
}
你可以去掉花括号 {}
,并使用赋值操作符 =
来声明函数体。 当使用赋值操作符 =
时,Kotlin 会使用类型推断,因此你也可以省略返回类型。这样 sum()
函数就变成了一行:
fun sum(x: Int, y: Int) = x + y
fun main() {
println(sum(1, 2))
// 3
}
但是,如果你希望其他开发者能快速理解你的代码,即使使用赋值操作符 =
,明确定义返回类型也是一个好主意。
函数中的提前返回 要停止函数中代码的进一步处理,可以使用 return
关键字。 以下示例使用 if
来在条件表达式为真时提前返回函数:
// 已注册的用户名列表
val registeredUsernames = mutableListOf("john_doe", "jane_smith")
// 已注册的邮箱列表
val registeredEmails = mutableListOf("john@example.com", "jane@example.com")
fun registerUser(username: String, email: String): String {
// 如果用户名已经被占用,则提前返回
if (username in registeredUsernames) {
return "用户名已被占用。请使用其他用户名。"
}
// 如果邮箱已经注册,则提前返回
if (email in registeredEmails) {
return "邮箱已注册。请使用其他邮箱。"
}
// 如果用户名和邮箱都没有被占用,则继续注册
registeredUsernames.add(username)
registeredEmails.add(email)
return "用户注册成功: $username"
}
fun main() {
println(registerUser("john_doe", "newjohn@example.com"))
// 用户名已被占用。请使用其他用户名。
println(registerUser("new_user", "newuser@example.com"))
// 用户注册成功: new_user
}
函数练习
练习 1 编写一个名为 circleArea
的函数,该函数以整数格式的圆半径作为参数,并输出该圆的面积。
import kotlin.math.PI
// 在这里写下你的代码
fun main() {
println(circleArea(2))
}
import kotlin.math.PI
fun circleArea(radius: Int): Double {
return PI * radius * radius
}
fun main() {
println(circleArea(2)) // 12.566370614359172
}
练习 2 将前一个练习中的 circleArea
函数重写为单表达式函数。
import kotlin.math.PI
// 在这里写下你的代码
fun main() {
println(circleArea(2))
}
import kotlin.math.PI
fun circleArea(radius: Int): Double = PI * radius * radius
fun main() {
println(circleArea(2)) // 12.566370614359172
}
练习 3 你有一个函数,它将以小时、分钟和秒表示的时间间隔转换为秒。 在大多数情况下,你只需要传递一个或两个函数参数,而其余参数默认为 0。 通过使用默认参数值和具名参数来改进函数和调用代码,以使代码更易读。
fun intervalInSeconds(hours: Int, minutes: Int, seconds: Int) =
((hours * 60) + minutes) * 60 + seconds
fun main() {
println(intervalInSeconds(1, 20, 15))
println(intervalInSeconds(0, 1, 25))
println(intervalInSeconds(2, 0, 0))
println(intervalInSeconds(0, 10, 0))
println(intervalInSeconds(1, 0, 1))
}
fun intervalInSeconds(hours: Int = 0, minutes: Int = 0, seconds: Int = 0) =
((hours * 60) + minutes) * 60 + seconds
fun main() {
println(intervalInSeconds(1, 20, 15))
println(intervalInSeconds(minutes = 1, seconds = 25))
println(intervalInSeconds(hours = 2))
println(intervalInSeconds(minutes = 10))
println(intervalInSeconds(hours = 1, seconds = 1))
}
Lambda 表达式 Kotlin 允许你通过使用 Lambda 表达式编写更简洁的函数代码。
例如,以下 uppercaseString()
函数:
fun uppercaseString(text: String): String {
return text.uppercase()
}
fun main() {
println(uppercaseString("hello"))
// HELLO
}
也可以写成 Lambda 表达式的形式:
fun main() {
val upperCaseString = { text: String -> text.uppercase() }
println(upperCaseString("hello"))
// HELLO
}
Lambda 表达式在初看时可能难以理解,所以我们来详细拆解一下。 Lambda 表达式写在花括号 {}
中。
在 Lambda 表达式中,你需要写:
在前面的示例中:
text
是函数的参数。
text
的类型是 String
。
函数返回对 text
调用的 .uppercase()
函数的结果。
整个 Lambda 表达式被赋值给 upperCaseString
变量,使用赋值操作符 =
。
Lambda 表达式通过将变量 upperCaseString
当作函数调用,并将字符串 "hello"
作为参数来调用。
println()
函数打印结果。
如果你声明一个没有参数的 Lambda,则无需使用 ->
。例如:
{ println("Log message") }
Lambda 表达式可以以多种方式使用。你可以:
传递给另一个函数 一个很好的例子是将 Lambda 表达式传递给函数,例如在集合上使用 .filter()
函数:
fun main() {
//sampleStart
val numbers = listOf(1, -2, 3, -4, 5, -6)
val positives = numbers.filter ({ x -> x > 0 })
val isNegative = { x: Int -> x < 0 }
val negatives = numbers.filter(isNegative)
println(positives)
// [1, 3, 5]
println(negatives)
// [-2, -4, -6]
//sampleEnd
}
.filter()
函数接受 Lambda 表达式作为谓词:
这个示例演示了两种将 Lambda 表达式传递给函数的方式:
如果 Lambda 表达式是唯一的函数参数,你可以省略函数的圆括号 ()
:
val positives = numbers.filter { x -> x > 0 }
这是一种 尾随 Lambda 的示例,详细讨论请参见本章末尾。
另一个好的例子是使用 .map()
函数来转换集合中的项目:
fun main() {
//sampleStart
val numbers = listOf(1, -2, 3, -4, 5, -6)
val doubled = numbers.map { x -> x * 2 }
val isTripled = { x: Int -> x * 3 }
val tripled = numbers.map(isTripled)
println(doubled)
// [2, -4, 6, -8, 10, -12]
println(tripled)
// [3, -6, 9, -12, 15, -18]
//sampleEnd
}
.map()
函数接受 Lambda 表达式作为转换函数:
函数类型 在你学习 从函数中返回 Lambda 表达式 之前,你首先需要理解什么是 函数类型 。
你已经了解了基本类型,但函数本身也有一个类型。 Kotlin 的类型推断可以从参数类型推断函数的类型。 但是有时你可能需要显式指定函数类型。 编译器需要函数类型,以便让函数知道这个内容是允许的还是不允许的。
函数类型的语法包括:
例如: (String) -> String
或 (Int, Int) -> Int
。
这是定义了 upperCaseString()
函数类型的 Lambda 表达式的样子:
val upperCaseString: (String) -> String = { text -> text.uppercase() }
fun main() {
println(upperCaseString("hello"))
// HELLO
}
如果你的 Lambda 表达式没有参数,那么圆括号 ()
留空。例如: () -> Unit
你必须在 Lambda 表达式中或作为函数类型声明参数和返回类型。 否则,编译器将无法知道你的 Lambda 表达式的类型。
例如,以下代码将无法工作:
val upperCaseString = { str -> str.uppercase() }
从函数中返回 Lambda 表达式 Lambda 表达式可以从函数中返回。 为了让编译器理解返回的 Lambda 表达式的类型,你必须声明一个函数类型。
在以下示例中, toSeconds()
函数的函数类型是 (Int) -> Int
,因为它总是一个返回接受 Int
类型参数并返回 Int
值的 Lambda 表达式。
这个示例使用 when
表达式来确定在调用 toSeconds()
时返回哪个 Lambda 表达式:
fun toSeconds(time: String): (Int) -> Int = when (time) {
"hour" -> { value -> value * 60 * 60 }
"minute" -> { value -> value * 60 }
"second" -> { value -> value }
else -> { value -> value }
}
fun main() {
val timesInMinutes = listOf(2, 10, 15, 1)
val min2sec = toSeconds("minute")
val totalTimeInSeconds = timesInMinutes.map(min2sec).sum()
println("Total time is $totalTimeInSeconds secs")
// Total time is 1680 secs
}
单独调用 Lambda 表达式可以通过在花括号 {}
后添加圆括号 ()
并在圆括号内包含任何参数来单独调用:
fun main() {
//sampleStart
println({ text: String -> text.uppercase() }("hello"))
// HELLO
//sampleEnd
}
尾随 Lambda 表达式 正如你已经看到的那样,如果 Lambda 表达式是唯一的函数参数,你可以省略函数的圆括号 ()
。 如果 Lambda 表达式作为函数的最后一个参数传递,那么该表达式可以写在函数圆括号 ()
之外。 在这两种情况下,这种语法被称为 尾随 Lambda 表达式 。
例如, .fold()
函数接受一个初始值和一个操作:
fun main() {
//sampleStart
// 初始值为零。
// 操作将初始值与列表中的每个项目进行累加。
println(listOf(1, 2, 3).fold(0, { x, item -> x + item })) // 6
// 或者,使用尾随 Lambda 表达式的形式
println(listOf(1, 2, 3).fold(0) { x, item -> x + item }) // 6
//sampleEnd
}
有关 Lambda 表达式的更多信息,请参见 Lambda 表达式和匿名函数 。
我们旅程的下一步是学习 Kotlin 中的 类 。
Lambda 表达式练习
练习 1 你有一个 web 服务支持的动作列表,一个请求的公共前缀,以及一个特定资源的 ID。 要对 ID 为 5 的资源请求一个 title
动作,你需要创建以下 URL: https://example.com/book-info/5/title
。 使用 lambda 表达式从动作列表中创建 URL 列表。
fun main() {
val actions = listOf("title", "year", "author")
val prefix = "https://example.com/book-info"
val id = 5
val urls = // 在这里写下你的代码
println(urls)
}
fun main() {
val actions = listOf("title", "year", "author")
val prefix = "https://example.com/book-info"
val id = 5
val urls = actions.map { action -> "$prefix/$id/$action" }
println(urls)
}
练习 2 编写一个函数,该函数接收一个 Int
值和一个动作(一个类型为 () -> Unit
的函数),然后重复执行该动作指定的次数。 然后使用这个函数打印 “Hello” 5 次。
fun repeatN(n: Int, action: () -> Unit) {
// 在这里写下你的代码
}
fun main() {
// 在这里写下你的代码
}
fun repeatN(n: Int, action: () -> Unit) {
for (i in 1..n) {
action()
}
}
fun main() {
repeatN(5) {
println("Hello")
}
}
Last modified: 26 十一月 2024