函数¶
Kotlin 中函数是一等语言结构。函数可以定义在顶层、类中、对象中,也可以作为值传递。对 Java 开发者来说,最大的变化是:很多以前必须放进工具类或静态方法里的逻辑,在 Kotlin 中可以直接用顶层函数、扩展函数和高阶函数表达。
基本声明¶
fun sum(a: Int, b: Int): Int {
return a + b
}
函数声明由几部分组成:
fun关键字。- 函数名。
- 参数列表。
- 返回类型。
- 函数体。
表达式函数体¶
当函数体只有一个表达式时,可以简写:
fun sum(a: Int, b: Int) = a + b
返回类型通常可以推断出来。公共 API 中,为了可读性和二进制兼容考虑,复杂函数建议显式写返回类型。
Unit¶
没有有意义返回值时,返回类型是 Unit:
fun log(message: String): Unit {
println(message)
}
通常省略:
fun log(message: String) {
println(message)
}
Java 的 void 不是类型;Kotlin 的 Unit 是类型。这让 Kotlin 的泛型和函数类型更统一。
默认参数¶
fun connect(host: String, port: Int = 443, secure: Boolean = true) {
println("$host:$port secure=$secure")
}
connect("example.com")
connect("example.com", 80, false)
Java 通常通过重载实现同样效果:
void connect(String host) {
connect(host, 443, true);
}
Kotlin 默认参数能减少大量重载方法。不过如果函数要被 Java 调用,默认参数不会天然变成 Java 重载;需要时可以使用 @JvmOverloads。
命名参数¶
connect(
host = "example.com",
secure = false,
port = 80
)
命名参数可以提高可读性,尤其适合多个同类型参数:
fun resize(width: Int, height: Int)
resize(width = 1920, height = 1080)
对 Java 方法调用命名参数通常不可用,因为 Java 字节码不总是保留稳定的参数名契约。
可变参数:vararg¶
fun printAll(vararg messages: String) {
for (message in messages) {
println(message)
}
}
printAll("A", "B", "C")
如果已有数组,需要展开传入:
val values = arrayOf("A", "B")
printAll(*values)
* 是展开运算符。Java 中数组可以直接传给 varargs;Kotlin 需要显式展开,是为了让调用意图更清楚。
局部函数¶
函数可以定义在函数内部:
fun saveUser(name: String, email: String) {
fun requireNotBlank(value: String, field: String) {
require(value.isNotBlank()) { "$field 不能为空" }
}
requireNotBlank(name, "name")
requireNotBlank(email, "email")
println("保存用户")
}
局部函数适合封装只在当前函数中使用的校验逻辑,避免污染类或文件级 API。
函数类型¶
函数可以作为值:
val operation: (Int, Int) -> Int = { a, b -> a + b }
println(operation(1, 2))
(Int, Int) -> Int 表示接收两个 Int,返回一个 Int 的函数。
高阶函数¶
接收函数作为参数,或返回函数的函数,叫高阶函数:
fun calculate(a: Int, b: Int, operation: (Int, Int) -> Int): Int {
return operation(a, b)
}
val result = calculate(10, 20) { x, y -> x + y }
Java 8 以后可以用函数式接口和 Lambda 做类似事情,但 Kotlin 的函数类型是语言内建概念,写法更直接。
尾随 Lambda¶
如果函数的最后一个参数是函数类型,调用时可以把 Lambda 放到括号外:
val names = listOf("Ada", "Grace", "Linus")
names.filter { it.length > 3 }
.map { it.uppercase() }
.forEach { println(it) }
这也是 Kotlin 集合 API 读起来像流水线的原因。
单表达式 Lambda 与 it¶
只有一个参数时,可以用隐式参数 it:
val longNames = names.filter { it.length > 3 }
如果 Lambda 较长,建议显式命名参数:
val longNames = names.filter { name ->
name.length > 3 && name.startsWith("G")
}
扩展函数预告¶
Kotlin 可以给已有类型“增加”函数:
fun String.firstCharOrNull(): Char? =
if (isEmpty()) null else this[0]
println("Kotlin".firstCharOrNull())
扩展函数不会真的修改原类,也不会突破封装访问私有成员。它更像一种静态解析的语法增强,适合组织工具方法。
实践建议¶
- 简单函数优先使用表达式函数体。
- 公共 API 的复杂返回类型建议显式写出。
- 默认参数可以减少重载,但 Java 调用方需要额外考虑。
- Lambda 很长时不要过度使用
it。 - 工具方法可以优先考虑顶层函数或扩展函数,而不是创建
XxxUtils类。
参考¶
- 官方函数文档:https://kotlinlang.org/docs/functions.html