Android面试笔记-kotlin相关
1. kotlin相对于Java的优
kotlin是一种比较现代化的语言,融合了多种语言的优势。比如使用JVM的内存管理,空安全控制,lambda表达式,以及其他各种语法糖。
- 语法简洁,减少样板代码
- 空安全(Null Safety)
- 扩展函数(Extension Functions)
- 函数式编程支持,Lambda 和 高阶函数
- 协程(Coroutines)
- 智能类型转换(Smart Casts),is判断后屋再次强转
- 默认参数与命名参数,减少方法重载
- 与 Java 的完全互操作性
- 现代语言特性,自动拆解、内联函数、密封类
- Google官方支持与社区生态
2.高阶函数
高阶函数是可以接收函数作为参数或返回函数作为结果的函数
- 函数类型的引用
- Lambda表达式定义匿名函数
- 函数引用,::+函数名,传递函数引用
- 内联函数
- 标准库高阶函数操作符
3.内联函数
内联函数(Inline Functions) 是一种通过 inline 关键字修饰的函数,主要用于优化高阶函数(尤其是包含 Lambda 参数的函数)的性能。它的核心原理是在编译时将函数体直接插入到调用处,从而减少函数调用和 Lambda 表达式带来的额外开销。
适用场景
- 高阶函数:当函数参数是 Lambda 时,内联可以避免创建匿名类对象(减少内存开销)。
- 性能敏感代码:例如频繁调用的工具函数。
- 结合
reified类型参数:在泛型中保留类型信息
注意事项
- 代码膨胀:内联函数会导致函数体被复制到多个调用处,如果函数体较大或调用频繁,可能增加生成的字节码大小。应避免内联大型函数。
- 限制:内联函数不能直接访问外层类的私有成员,部分操作(如递归调用)无法内联
noinline
内联函数的某个参数是函数,则该参数默认也是 inline 的,当需要禁止这个函数参数inline,比如需要被其他非inline函数调用或者返回值,就需要使用noinline标记。
crossinline
在内联函数中的 lambda 表达式参数,可以直接使用不带标签的 return,返回的是调用内联函数的外部函数,而不是内联函数本身,默认就是非局部返回。
crossinline 标记禁止内联函数的 lambda 表达式参数使用非局部返回,即禁用return
4.扩展函数
原理:扩展函数在编译时会被转换为静态方法,接收者对象(即扩展的目标类实例)作为该静态方法的第一个参数。
- 无法访问私有成员
- 静态解析(非多态) :扩展函数的调用在编译时根据接收者的声明类型确定,而非运行时实际类型
open class Parent
class Child : Parent()
fun Parent.foo() = "Parent"
fun Child.foo() = "Child"
val obj: Parent = Child()
println(obj.foo()) // 输出 "Parent",而非 "Child"
- 优先级低于成员方法
- 扩展属性实现,扩展属性同样通过静态方法实现,但不会有实际字段存储,必须通过
getter(和setter,若为var) - 可空接收者支持
- 扩展函数需通过导入(
import)才能使用,若存在同名冲突,需使用全限定名或别名
5.标准库中的作用域函数
Kotlin 标准库中的这些作用域函数(let, also, run, with, apply, repeat)在对象操作和代码块执行中有不同的用途和特点。它们的核心区别在于 上下文对象的引用方式 和 返回值类型
let
- 上下文对象:通过
it引用。 - 返回值:Lambda 表达式的结果(最后一行)。
- 使用场景:
- 处理可空对象(避免空指针)。
- 链式调用中转换或处理对象。
- 示例:
val length = text?.let { println("Processing: $it") it.length // 返回长度 } ?: 0
also
- 上下文对象:通过
it引用。 - 返回值:返回对象本身(
this)。 - 使用场景:
- 链式调用中执行副作用操作(如日志、修改对象属性)。
- 示例:
val list = mutableListOf(1, 2, 3).also { println("Initial list: $it") it.add(4) // 修改对象 }
run
- 上下文对象:通过
this引用(可省略)。 - 返回值:Lambda 表达式的结果。
- 变体:
- 扩展函数形式:
obj.run { ... } - 非扩展函数形式:
run { ... }(无上下文对象)。
- 扩展函数形式:
- 使用场景:
- 初始化对象并计算结果。
- 替代
let但需要省略it。
- 示例:
val result = Person().run { name = "Alice" // 直接访问属性(this 可省略) age = 30 getInfo() // 返回结果 }
with
- 上下文对象:通过
this引用(参数传入)。 - 返回值:Lambda 表达式的结果。
- 特点:非扩展函数,直接传入对象。
- 使用场景:
- 对对象进行多次操作,无需返回对象本身。
- 示例:
val person = Person("Bob", 25) val info = with(person) { age += 1 // 修改属性 "Name: $name, Age: $age" // 返回结果 }
apply
- 上下文对象:通过
this引用。 - 返回值:返回对象本身(
this)。 - 使用场景:
- 对象初始化(如配置属性)。
- 示例:
val person = Person().apply { name = "Charlie" age = 40 // 返回 this(Person 对象) }
repeat
- 用途:重复执行代码块指定次数。
- 返回值:
Unit(无返回值)。 - 使用场景:
- 替代简单的
for循环。
- 替代简单的
- 示例:
repeat(3) { i -> println("Iteration $i") }
对比表格
| 函数 | 上下文对象 | 返回值 | 扩展函数 | 典型场景 |
|---|---|---|---|---|
let | it | Lambda 结果 | 是 | 处理可空对象、链式转换 |
also | it | 对象本身 | 是 | 链式副作用操作 |
run | this | Lambda 结果 | 是/否 | 初始化对象并计算、替代 let |
with | this | Lambda 结果 | 否 | 对已有对象进行多次操作 |
apply | this | 对象本身 | 是 | 对象初始化配置 |
repeat | 无 | Unit | 否 | 替代简单循环 |
选择指南
- 需要返回对象本身 →
apply或also。- 用
apply配置属性(this可省略)。 - 用
also执行副作用(如日志)。
- 用
- 需要返回计算结果 →
let、run或with。- 处理可空对象 →
let。 - 需要
this上下文 →run或with。
- 处理可空对象 →
- 重复执行代码块指定次数 →
repeat。
6.kotlin的协变和逆变
- 协变
out:表示类型参数仅出现在输出位置(如返回值),可以父多类型的变量引用 - 逆变
in:表示类型参数仅出现在输入位置(如方法参数),可以被子类型的变量引用