第1章:函数与Lambda——Kotlin的函数式编程基石

各位同学,欢迎来到《Kotlin Android 开发(Jetpack Compose)从入门到精通》的第一章。今天咱们聊聊函数和Lambda。说实话,函数这东西,写Java的时候天天用,但Kotlin把它玩出了新花样。我个人觉得,理解好这一章,后面学Compose会轻松很多——因为Compose里到处都是Lambda。

1.1 函数定义与调用

Kotlin的函数定义,语法上比Java简洁不少。你看这个:

fun add(a: Int, b: Int): Int {
    return a + b
}

调用起来也很直接:

val result = add(3, 5)  // result = 8

嗯,这里有个小细节:fun关键字是必须的,参数类型写在冒号后面,返回值类型也是。跟Java比,少了publicstatic这些修饰符——因为Kotlin默认就是public的。

小提示: 如果函数体只有一行代码,可以省略花括号,用等号直接赋值。这个后面会讲到。

1.2 参数默认值与命名参数

这个特性我特别喜欢。写Java的时候,为了给参数提供默认值,经常要写一堆重载方法。Kotlin直接搞定:

fun createUser(name: String, age: Int = 18, city: String = "北京") {
    println("姓名:$name,年龄:$age,城市:$city")
}

调用的时候,你可以只传必填参数:

createUser("张三")  // 输出:姓名:张三,年龄:18,城市:北京

也可以指定某些参数:

createUser("李四", city = "上海")  // 输出:姓名:李四,年龄:18,城市:上海

命名参数的好处是,你不用记住参数的顺序。我在项目中遇到过好几次,因为参数顺序搞混导致的bug——用了命名参数后,这类问题再也没出现过。

注意: 命名参数在调用Java函数时不能用,因为Java字节码不保留参数名信息。

1.3 单表达式函数

刚才提到,函数体只有一行时可以简化。这就是单表达式函数:

fun double(x: Int): Int = x * 2

甚至可以省略返回值类型,让编译器自动推断:

fun double(x: Int) = x * 2

简洁吧?我写工具类函数时特别喜欢用这种写法。但要注意,如果函数逻辑复杂,还是老老实实写完整函数体比较好——可读性更重要。

1.4 高阶函数

高阶函数,说白了就是能接收函数作为参数,或者返回一个函数的函数。这是函数式编程的核心概念。

举个例子:

fun operate(a: Int, b: Int, operation: (Int, Int) -> Int): Int {
    return operation(a, b)
}

调用时,你可以传入不同的操作:

val sum = operate(3, 5) { x, y -> x + y }      // 8
val product = operate(3, 5) { x, y -> x * y }  // 15

你想想看,这比写一堆if-else优雅多了。我在做网络请求封装时,经常用高阶函数来处理成功和失败的回调。

1.5 Lambda表达式

Lambda就是匿名函数的一种简洁写法。基本语法是这样的:

{ 参数列表 -> 函数体 }

比如:

val sum: (Int, Int) -> Int = { x, y -> x + y }

如果Lambda只有一个参数,可以用it来隐式引用它:

val numbers = listOf(1, 2, 3, 4, 5)
val doubled = numbers.map { it * 2 }  // [2, 4, 6, 8, 10]

这里有个坑,我曾经踩过:如果Lambda是函数的最后一个参数,可以把它移到括号外面。这就是所谓的「尾Lambda语法」:

// 常规写法
numbers.map({ it * 2 })

// 尾Lambda写法(更常见)
numbers.map { it * 2 }

在Compose中,这种写法随处可见。你写Button(onClick = { ... })时,其实就是在用尾Lambda。

1.6 内联函数

Lambda虽然好用,但有个性能问题:每次创建Lambda都会生成一个匿名内部类对象。如果频繁调用,会有额外的内存开销。

内联函数就是解决这个问题的。用inline关键字标记函数后,编译器会把函数体直接复制到调用处,避免创建对象:

inline fun measureTime(action: () -> Unit) {
    val start = System.currentTimeMillis()
    action()
    val end = System.currentTimeMillis()
    println("耗时:${end - start}ms")
}

调用时:

measureTime {
    // 一些耗时操作
    Thread.sleep(100)
}

编译后,measureTime的代码会被直接插入到调用位置,不会创建Lambda对象。性能更好。

核心要点: 内联函数适合参数是Lambda的情况,尤其是这个Lambda会被频繁调用。但不要滥用——内联会增加字节码体积,大函数不适合内联。

知识体系总览

下面这张图,帮你理清本章的知识结构:

函数与Lambda 函数定义与调用 参数默认值/命名参数 单表达式函数 高阶函数 Lambda表达式 内联函数 函数式编程 → 代码更简洁、更灵活

总结

这一章我们走过了Kotlin函数的核心特性。从基本的定义调用,到参数默认值、命名参数,再到单表达式函数、高阶函数、Lambda和内联函数。说白了,Kotlin在函数这块下了很大功夫,让代码写起来更舒服,跑起来更高效。

我个人建议,刚开始学的时候,先把Lambda和高阶函数用熟。这两个在Compose里天天见。内联函数可以先了解,等遇到性能瓶颈时再深入优化。

好,今天就到这里。下一章我们聊集合与泛型——又是Kotlin的一大亮点。


公众号:蓝海资料掘金营,微信deep3321