第一章:Kotlin 基础概念面试题答案
1. Kotlin 基础
1.1 什么是Kotlin?Kotlin的特点是什么?
答案:
Kotlin是由JetBrains开发的一门静态类型编程语言,运行在Java虚拟机上,也可以编译成JavaScript或Native代码。
Kotlin的主要特点:
-
简洁性
- 语法简洁,代码量比Java减少约20-30%
- 支持类型推断,减少冗余代码
- 支持单表达式函数、数据类等语法糖
-
安全性
- 空安全设计,编译时检查空指针异常
- 不可变集合默认,减少并发问题
- 类型系统更严格
-
互操作性
- 100%兼容Java,可以调用Java代码
- 可以逐步迁移,无需重写整个项目
- 支持Java库和框架
-
函数式编程支持
- 支持Lambda表达式
- 支持高阶函数
- 支持不可变数据结构
-
工具支持
- 由JetBrains开发,IDE支持完善
- 编译速度快
- 支持增量编译
示例代码:
// 简洁的语法
data class User(val name: String, val age: Int)
// 空安全
var name: String? = null // 可空类型
name?.length // 安全调用
// 类型推断
val list = listOf(1, 2, 3) // 自动推断为List<Int>
1.2 Kotlin和Java的区别是什么?
答案:
Kotlin和Java的主要区别如下:
1. 语法简洁性
- Kotlin:语法更简洁,代码量更少
- Java:语法相对冗长
// Kotlin
data class User(val name: String, val age: Int)
// Java等价代码
public class User {
private String name;
private int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
// getter/setter/equals/hashCode/toString...
}
2. 空安全
- Kotlin:类型系统区分可空和非空类型,编译时检查
- Java:所有引用类型都可空,运行时可能NPE
// Kotlin
var name: String? = null // 可空
var name2: String = "Kotlin" // 非空
// Java
String name = null; // 可能NPE
3. 函数式编程
- Kotlin:原生支持Lambda、高阶函数
- Java:Java 8+才支持Lambda
// Kotlin
list.filter { it > 0 }.map { it * 2 }
// Java 8+
list.stream().filter(x -> x > 0).map(x -> x * 2).collect(Collectors.toList());
4. 扩展函数
- Kotlin:支持扩展函数,可以为现有类添加方法
- Java:不支持,需要通过工具类实现
// Kotlin
fun String.removeSpaces(): String = this.replace(" ", "")
// Java需要工具类
public class StringUtils {
public static String removeSpaces(String str) {
return str.replace(" ", "");
}
}
5. 数据类
- Kotlin:
data class自动生成equals、hashCode、toString等 - Java:需要手动实现或使用Lombok
6. 默认参数和命名参数
- Kotlin:支持默认参数和命名参数
- Java:不支持,需要方法重载
// Kotlin
fun greet(name: String, greeting: String = "Hello") {
println("$greeting, $name")
}
greet("Kotlin", greeting = "Hi")
// Java需要多个重载方法
7. 字符串模板
- Kotlin:支持字符串模板
"Hello, $name" - Java:需要字符串拼接或String.format
8. 类型推断
- Kotlin:支持类型推断,
val list = listOf(1, 2, 3) - Java:需要显式类型(Java 10+支持var)
9. 协程支持
- Kotlin:原生支持协程,异步编程更简单
- Java:需要CompletableFuture或第三方库
10. 编译目标
- Kotlin:可以编译为JVM字节码、JavaScript、Native
- Java:主要编译为JVM字节码
1.3 为什么选择Kotlin而不是Java?
答案:
选择Kotlin的主要原因:
1. 代码简洁性
- 代码量减少20-30%,提高开发效率
- 语法更现代化,可读性更好
- 减少样板代码
2. 空安全
- 编译时检查空指针,减少运行时崩溃
- 提高代码质量和稳定性
- 减少调试时间
3. 互操作性
- 100%兼容Java,可以逐步迁移
- 可以使用现有Java库和框架
- 团队可以混合使用Java和Kotlin
4. Android官方支持
- Google在2017年宣布Kotlin为Android官方开发语言
- Android Studio提供完善的Kotlin支持
- 大量Android库支持Kotlin
5. 函数式编程
- 支持Lambda、高阶函数
- 代码更简洁、表达力更强
- 支持不可变数据结构
6. 扩展函数
- 可以为现有类添加方法
- 不需要继承或装饰器模式
- 代码组织更灵活
7. 协程支持
- 原生支持协程,异步编程更简单
- 比Java的CompletableFuture更易用
- 性能更好,资源占用更少
8. 工具支持
- JetBrains开发,IDE支持完善
- 编译速度快
- 支持增量编译
9. 社区和生态
- 活跃的社区
- 丰富的学习资源
- 大量开源库支持
10. 未来趋势
- 现代语言特性
- 持续更新和改进
- 跨平台支持(Kotlin Multiplatform)
适用场景:
- Android开发(官方推荐)
- 后端开发(Spring支持Kotlin)
- 需要与Java互操作的项目
- 新项目或逐步迁移的项目
1.4 Kotlin的编译过程是什么?
答案:
Kotlin的编译过程如下:
1. 源代码编译
Kotlin源代码(.kt) → Kotlin编译器 → 字节码(.class)
2. 编译步骤
步骤1:词法分析(Lexical Analysis)
- 将源代码分解为Token(关键字、标识符、操作符等)
- 识别语法单元
步骤2:语法分析(Syntax Analysis)
- 将Token组织成抽象语法树(AST)
- 检查语法正确性
步骤3:语义分析(Semantic Analysis)
- 类型检查
- 空安全检查
- 作用域检查
- 生成符号表
步骤4:代码生成(Code Generation)
- 将AST转换为JVM字节码
- 优化代码
- 生成.class文件
3. 编译目标
Kotlin可以编译为多种目标:
JVM目标(最常见)
Kotlin源码 → Kotlin编译器 → JVM字节码 → Java虚拟机执行
JavaScript目标
Kotlin源码 → Kotlin/JS编译器 → JavaScript代码 → 浏览器执行
Native目标
Kotlin源码 → Kotlin/Native编译器 → 原生机器码 → 直接执行
4. 编译工具
- kotlinc:命令行编译器
- Gradle/Maven插件:构建工具集成
- IDE集成:IntelliJ IDEA、Android Studio
5. 编译特点
增量编译
- 只编译修改的文件
- 提高编译速度
类型推断
- 编译时推断类型
- 生成明确的字节码
空安全检查
- 编译时检查可空类型
- 生成空检查代码
内联函数
- 编译时展开函数体
- 减少函数调用开销
6. 编译产物
JVM目标
.class文件(JVM字节码)- 可以反编译为Java代码(但可读性较差)
JavaScript目标
.js文件- 可以调用JavaScript库
Native目标
- 可执行文件或库
- 不依赖虚拟机
7. 与Java互操作
Kotlin编译后的字节码与Java完全兼容:
- 可以调用Java类和方法
- Java可以调用Kotlin代码
- 共享运行时环境(JVM)
示例:
// Kotlin源码
fun main() {
println("Hello Kotlin")
}
// 编译后的字节码(反编译为Java)
public static final void main() {
String var0 = "Hello Kotlin";
System.out.println(var0);
}
1.5 Kotlin如何与Java互操作?
答案:
Kotlin与Java的互操作非常完善,主要体现在以下几个方面:
1. 调用Java代码
Kotlin可以直接调用Java类、方法和属性:
// 调用Java类
val list = ArrayList<String>()
list.add("Kotlin")
// 调用Java方法
val date = Date()
val time = date.time
// 调用Java静态方法
val result = Math.max(1, 2)
2. Java调用Kotlin代码
Java可以调用Kotlin的类、函数和属性:
// Kotlin代码
class User(val name: String, val age: Int)
fun greet(name: String): String {
return "Hello, $name"
}
// Java调用
User user = new User("Kotlin", 30);
String greeting = GreetKt.greet("Kotlin");
3. 类型映射
Kotlin和Java的类型自动映射:
| Java类型 | Kotlin类型 |
|---|---|
Object | Any |
void | Unit |
int | Int |
Integer | Int? |
String | String |
List | List |
ArrayList | MutableList |
4. 空安全互操作
Kotlin调用Java
- Java类型在Kotlin中视为平台类型(可空或非空)
- 需要手动处理空安全
// Java方法返回String(可能为null)
fun processJavaString(str: String?) {
str?.let { println(it) }
}
Java调用Kotlin
- 使用
@Nullable和@NonNull注解
// Kotlin
fun process(@Nullable name: String?): String {
return name ?: "Unknown"
}
5. 注解处理
Kotlin提供注解控制Java互操作:
@JvmStatic
class User {
companion object {
@JvmStatic
fun create(): User = User()
}
}
// Java调用
User user = User.create(); // 可以直接调用
@JvmField
class User {
@JvmField
val name: String = "Kotlin"
}
// Java调用
String name = user.name; // 直接访问字段
@JvmOverloads
@JvmOverloads
fun greet(name: String, greeting: String = "Hello") {
println("$greeting, $name")
}
// Java会生成多个重载方法
greet("Kotlin");
greet("Kotlin", "Hi");
@JvmName
@file:JvmName("StringUtils")
fun String.removeSpaces(): String = this.replace(" ", "")
// Java调用
StringUtils.removeSpaces("hello world");
6. 集合互操作
Kotlin集合和Java集合可以互转:
// Kotlin集合转Java集合
val kotlinList = listOf(1, 2, 3)
val javaList: java.util.List<Int> = kotlinList
// Java集合转Kotlin集合
val javaList = ArrayList<Int>()
val kotlinList: List<Int> = javaList
7. 异常处理
Kotlin的异常处理与Java兼容:
// Kotlin调用Java(可能抛出异常)
@Throws(IOException::class)
fun readFile(): String {
// Java代码可能抛出IOException
}
// Java调用Kotlin
try {
readFile();
} catch (IOException e) {
// 处理异常
}
8. 泛型互操作
Kotlin和Java的泛型可以互操作:
// Kotlin
fun processList(list: List<String>) {
// 处理
}
// Java调用
List<String> list = new ArrayList<>();
processList(list);
9. 互操作最佳实践
- 逐步迁移:可以混合使用Java和Kotlin
- 空安全处理:Java代码返回的类型需要手动处理空安全
- 使用注解:合理使用
@JvmStatic、@JvmField等注解 - 类型映射:了解类型映射规则
- 测试验证:确保互操作代码正常工作
10. 实际应用
- Android开发:Kotlin和Java混合使用
- Spring Boot:支持Kotlin,可以混合使用
- Gradle构建:Kotlin DSL和Groovy可以共存
2. 变量和类型
1.6 Kotlin的变量声明方式(var、val)是什么?
答案:
Kotlin使用var和val关键字声明变量:
1. var(可变变量)
var用于声明可变变量,值可以修改:
var name: String = "Kotlin"
name = "Java" // 可以修改
var age = 25 // 类型推断
age = 26 // 可以修改
2. val(不可变变量)
val用于声明不可变变量,值不能修改(类似Java的final):
val name: String = "Kotlin"
// name = "Java" // 编译错误,不能修改
val age = 25
// age = 26 // 编译错误
3. 区别对比
| 特性 | var | val |
|---|---|---|
| 可变性 | 可变 | 不可变 |
| 重新赋值 | 可以 | 不可以 |
| 类似Java | 普通变量 | final变量 |
| 推荐使用 | 需要修改时 | 优先使用 |
4. 使用场景
使用val(推荐)
- 大多数情况下使用
val - 提高代码安全性
- 避免意外修改
- 支持函数式编程风格
val user = User("Kotlin", 30)
val list = listOf(1, 2, 3) // 不可变列表
使用var(必要时)
- 需要修改变量值时
- 循环计数器
- 状态变量
var count = 0
for (i in 1..10) {
count++ // 需要修改
}
5. 类型推断
Kotlin支持类型推断,可以省略类型声明:
// 显式类型
var name: String = "Kotlin"
val age: Int = 25
// 类型推断
var name = "Kotlin" // 推断为String
val age = 25 // 推断为Int
val list = listOf(1, 2, 3) // 推断为List<Int>
6. 延迟初始化
var的延迟初始化
var name: String? = null
name = "Kotlin" // 后续赋值
val的延迟初始化
// 方式1:使用lateinit(仅用于var)
lateinit var name: String
// 方式2:使用lazy(用于val)
val name: String by lazy {
"Kotlin"
}
7. 顶层变量
可以在文件顶层声明变量:
// 文件顶层
val PI = 3.14
var count = 0
fun main() {
println(PI)
count++
}
8. 最佳实践
- 优先使用val:除非需要修改,否则使用
val - 明确类型:复杂类型建议显式声明
- 避免可变状态:函数式编程风格,减少
var使用 - 合理使用类型推断:简单类型可以使用类型推断
1.7 var和val的区别是什么?
答案:
var和val的主要区别:
1. 可变性
var(可变)
var name = "Kotlin"
name = "Java" // ✅ 可以修改
val(不可变)
val name = "Kotlin"
// name = "Java" // ❌ 编译错误,不能修改
2. 内存模型
var
- 变量引用可以改变
- 可以指向不同的对象
val
- 变量引用不能改变
- 但对象内容可能可变(如果对象本身可变)
// val的引用不可变,但对象内容可变
val list = mutableListOf(1, 2, 3)
// list = mutableListOf(4, 5, 6) // ❌ 不能重新赋值
list.add(4) // ✅ 可以修改对象内容
3. 线程安全
val
- 更安全,避免并发修改
- 推荐在多线程环境使用
var
- 需要同步机制
- 可能产生竞态条件
4. 性能
val
- 编译器可能优化
- 不可变对象更安全
var
- 无特殊优化
- 可变对象需要更多检查
5. 使用场景
使用val(推荐)
// 常量
val PI = 3.14
// 配置
val config = Config()
// 集合(不可变)
val list = listOf(1, 2, 3)
使用var(必要时)
// 循环计数器
var i = 0
while (i < 10) {
i++
}
// 状态变量
var isLoaded = false
6. 与Java对比
| Kotlin | Java等价 |
|---|---|
val name = "Kotlin" | final String name = "Kotlin"; |
var name = "Kotlin" | String name = "Kotlin"; |
7. 最佳实践
- 默认使用val:除非需要修改
- 函数式风格:减少可变状态
- 明确意图:
val表示不可变,var表示可变 - 代码审查:检查
var的使用是否必要
1.8 Kotlin的基本数据类型有哪些?
答案:
Kotlin的基本数据类型分为两类:数字类型和其他类型。
1. 数字类型
Kotlin的数字类型都是类,不是原始类型:
| 类型 | 大小(位) | 最小值 | 最大值 |
|---|---|---|---|
Byte | 8 | -128 | 127 |
Short | 16 | -32768 | 32767 |
Int | 32 | -2³¹ | 2³¹-1 |
Long | 64 | -2⁶³ | 2⁶³-1 |
Float | 32 | IEEE 754 | |
Double | 64 | IEEE 754 |
示例:
val byte: Byte = 127
val short: Short = 32767
val int: Int = 2147483647
val long: Long = 9223372036854775807L
val float: Float = 3.14F
val double: Double = 3.141592653589793
2. 字符类型
Char
- 16位Unicode字符
- 使用单引号
val char: Char = 'A'
val unicode: Char = '\u0041' // 'A'
3. 布尔类型
Boolean
true或false
val isTrue: Boolean = true
val isFalse: Boolean = false
4. 字符串类型
String
- 不可变字符串
- 使用双引号
val str: String = "Kotlin"
val multiLine = """
Line 1
Line 2
""".trimIndent()
5. 类型转换
显式转换
val int: Int = 100
val long: Long = int.toLong()
val double: Double = int.toDouble()
隐式转换(不支持)
// ❌ Kotlin不支持隐式转换
val int: Int = 100
// val long: Long = int // 编译错误
// ✅ 必须显式转换
val long: Long = int.toLong()
6. 字面量
整数字面量
val decimal = 123
val hex = 0x0F
val binary = 0b00001011
val long = 123L
浮点数字面量
val double = 123.5
val float = 123.5F
val scientific = 1.23e10
字符和字符串字面量
val char = 'A'
val str = "Kotlin"
val raw = """
Multi-line
String
"""
7. 可空类型
所有基本类型都有可空版本:
val int: Int = 100
val intNullable: Int? = null
val double: Double = 3.14
val doubleNullable: Double? = null
8. 数组类型
Kotlin提供数组类型:
val intArray = intArrayOf(1, 2, 3)
val array = arrayOf(1, 2, 3)
val stringArray = arrayOf("a", "b", "c")
9. 与Java对比
| Kotlin | Java |
|---|---|
Int | int / Integer |
Long | long / Long |
Double | double / Double |
Boolean | boolean / Boolean |
Char | char / Character |
10. 类型推断
Kotlin支持类型推断:
// 推断为Int
val number = 100
// 推断为Double
val pi = 3.14
// 推断为String
val name = "Kotlin"
1.9 Kotlin的类型推断是什么?
答案:
类型推断(Type Inference)是Kotlin编译器自动推断变量或表达式类型的能力。
1. 基本概念
Kotlin编译器可以根据上下文自动推断类型,无需显式声明:
// 显式类型声明
val name: String = "Kotlin"
// 类型推断
val name = "Kotlin" // 编译器推断为String
2. 变量类型推断
基本类型推断
val int = 100 // 推断为Int
val double = 3.14 // 推断为Double
val float = 3.14F // 推断为Float
val long = 100L // 推断为Long
val boolean = true // 推断为Boolean
val char = 'A' // 推断为Char
val string = "Kotlin" // 推断为String
集合类型推断
val list = listOf(1, 2, 3) // 推断为List<Int>
val map = mapOf("a" to 1, "b" to 2) // 推断为Map<String, Int>
val set = setOf(1, 2, 3) // 推断为Set<Int>
3. 函数返回类型推断
单表达式函数
// 推断返回类型为Int
fun add(a: Int, b: Int) = a + b
// 等价于
fun add(a: Int, b: Int): Int {
return a + b
}
Lambda表达式
// 推断参数类型和返回类型
val square = { x: Int -> x * x } // 推断为(Int) -> Int
// 更简洁的写法
list.map { it * 2 } // it推断为Int
4. 泛型类型推断
// 推断泛型类型
val list = listOf(1, 2, 3) // List<Int>
val map = mapOf("a" to 1) // Map<String, Int>
// 函数泛型推断
fun <T> identity(value: T) = value
val result = identity(100) // 推断T为Int
5. 类型推断的限制
需要显式类型的场景:
- 公共API
// 公共函数建议显式类型
public fun process(data: String): Result {
// ...
}
- 复杂类型
// 复杂类型建议显式声明
val complex: Map<String, List<Int>> = mapOf(
"a" to listOf(1, 2),
"b" to listOf(3, 4)
)
- 可空类型
// 可空类型需要明确
val name: String? = null
- 递归函数
// 递归函数需要显式返回类型
fun factorial(n: Int): Int {
return if (n <= 1) 1 else n * factorial(n - 1)
}
6. 类型推断的优势
- 代码简洁:减少冗余代码
- 易于重构:修改类型时自动更新
- 减少错误:编译器自动检查类型
7. 类型推断示例
// 变量推断
val name = "Kotlin" // String
val age = 25 // Int
val isActive = true // Boolean
// 集合推断
val numbers = listOf(1, 2, 3) // List<Int>
val map = mapOf("key" to "value") // Map<String, String>
// 函数推断
fun multiply(a: Int, b: Int) = a * b // 返回Int
// Lambda推断
val double = { x: Int -> x * 2 } // (Int) -> Int
numbers.map { it * 2 } // it推断为Int
8. 最佳实践
- 简单类型:可以使用类型推断
- 复杂类型:建议显式声明
- 公共API:建议显式类型
- 可读性:平衡简洁性和可读性
1.10 Kotlin的可空类型(Nullable)是什么?
答案:
可空类型(Nullable Types)是Kotlin类型系统的核心特性,用于在编译时防止空指针异常。
1. 基本概念
Kotlin的类型系统区分可空和非空类型:
// 非空类型(默认)
var name: String = "Kotlin"
// name = null // ❌ 编译错误
// 可空类型(使用?)
var name: String? = "Kotlin"
name = null // ✅ 允许为null
2. 可空类型声明
使用?标记可空类型:
var name: String? = null
var age: Int? = null
var list: List<String>? = null
3. 空安全操作符
安全调用操作符(?.)
val name: String? = null
val length = name?.length // 如果name为null,返回null,不抛异常
Elvis操作符(?:)
val name: String? = null
val length = name?.length ?: 0 // 如果为null,返回0
非空断言操作符(!!)
val name: String? = "Kotlin"
val length = name!!.length // 强制非空,如果为null会抛异常
4. 空安全调用链
val user: User? = null
val street = user?.address?.street // 安全调用链
// 等价于Java的
// String street = user != null && user.address != null
// ? user.address.street : null;
5. 空检查
Kotlin编译器会进行空检查:
fun process(name: String?) {
if (name != null) {
// 在这个作用域内,name被视为非空
println(name.length) // 不需要?.
}
}
6. 智能转换(Smart Cast)
fun process(value: String?) {
if (value != null) {
// 编译器自动转换value为非空类型
println(value.length) // 直接使用,不需要?.
}
}
7. 可空类型的使用场景
从Java代码接收
// Java方法可能返回null
fun processJavaString(str: String?) {
str?.let { println(it) }
}
可选参数
fun greet(name: String, greeting: String? = null) {
val msg = greeting ?: "Hello"
println("$msg, $name")
}
集合中的可空
val list: List<String?> = listOf("a", null, "b")
val nonNullList = list.filterNotNull() // 过滤null
8. 与Java对比
| Java | Kotlin |
|---|---|
String name = null;(可能NPE) | String? name = null;(安全) |
if (name != null) { name.length(); } | name?.length |
String result = name != null ? name : "default"; | val result = name ?: "default" |
9. 最佳实践
- 优先使用非空类型:除非确实需要null
- 使用安全调用:避免使用
!! - 合理使用Elvis操作符:提供默认值
- 空检查:利用智能转换
10. 示例
// 可空类型声明
var name: String? = null
// 安全调用
val length = name?.length
// Elvis操作符
val displayName = name ?: "Unknown"
// 安全调用链
val user: User? = getUser()
val email = user?.profile?.email ?: "no-email"
// 空检查
if (name != null) {
println(name.length) // 智能转换
}
3. 字符串和模板
1.11 Kotlin的字符串模板是什么?
答案:
字符串模板(String Templates)是Kotlin中用于在字符串中嵌入变量和表达式的语法特性。
1. 基本语法
使用$符号嵌入变量或表达式:
// 简单变量
val name = "Kotlin"
val message = "Hello, $name" // "Hello, Kotlin"
// 表达式
val age = 25
val info = "Age: ${age + 1}" // "Age: 26"
2. 变量插值
使用$变量名:
val name = "Kotlin"
val greeting = "Hello, $name" // Hello, Kotlin
val price = 100
val total = "Price: $price" // Price: 100
3. 表达式插值
使用${表达式}:
val x = 10
val y = 20
val sum = "Sum: ${x + y}" // Sum: 30
val name = "Kotlin"
val length = "Length: ${name.length}" // Length: 6
// 函数调用
val upper = "Upper: ${name.uppercase()}" // Upper: KOTLIN
4. 嵌套表达式
val user = "Kotlin"
val score = 95
val result = "User: ${user.uppercase()}, Score: ${if (score >= 90) "Excellent" else "Good"}"
// User: KOTLIN, Score: Excellent
5. 原始字符串(Raw String)
使用三引号"""支持多行字符串:
val text = """
Line 1
Line 2
Line 3
""".trimIndent()
// 输出:
// Line 1
// Line 2
// Line 3
6. 转义字符
在字符串模板中使用$需要使用转义:
val price = 100
val message = "Price: \$$price" // Price: $100
// 或者使用表达式
val message2 = "Price: ${'$'}$price" // Price: $100
7. 字符串模板的优势
1. 简洁性
// Kotlin字符串模板
val name = "Kotlin"
val message = "Hello, $name"
// Java字符串拼接
String name = "Java";
String message = "Hello, " + name;
2. 性能
- 编译时优化
- 比字符串拼接性能更好
3. 可读性
- 代码更清晰
- 易于理解和维护
8. 与Java对比
| Kotlin | Java |
|---|---|
"Hello, $name" | "Hello, " + name |
"Sum: ${x + y}" | "Sum: " + (x + y) |
"""多行""" | "多行1\n多行2" |
9. 常见用法
日志输出
val user = "Kotlin"
val action = "login"
println("User: $user, Action: $action")
SQL查询
val table = "users"
val id = 123
val query = "SELECT * FROM $table WHERE id = $id"
HTML生成
val title = "Kotlin"
val html = """
<html>
<head><title>$title</title></head>
<body>Content</body>
</html>
"""
10. 最佳实践
- 简单变量:使用
$变量名 - 表达式:使用
${表达式} - 复杂表达式:使用
${}包裹 - 性能考虑:避免在循环中频繁使用复杂表达式
1.12 Kotlin的字符串拼接方式有哪些?
答案:
Kotlin提供了多种字符串拼接方式:
1. 字符串模板(推荐)
使用$符号拼接:
val name = "Kotlin"
val age = 25
val message = "Name: $name, Age: $age" // Name: Kotlin, Age: 25
2. 字符串连接符(+)
使用+运算符:
val name = "Kotlin"
val message = "Hello, " + name // Hello, Kotlin
val firstName = "John"
val lastName = "Doe"
val fullName = firstName + " " + lastName // John Doe
3. StringBuilder
使用StringBuilder类(适合大量拼接):
val builder = StringBuilder()
builder.append("Hello")
builder.append(", ")
builder.append("Kotlin")
val message = builder.toString() // Hello, Kotlin
// 链式调用
val message2 = StringBuilder()
.append("Hello")
.append(", ")
.append("Kotlin")
.toString()
4. buildString函数
使用Kotlin标准库的buildString函数:
val message = buildString {
append("Hello")
append(", ")
append("Kotlin")
} // Hello, Kotlin
5. joinToString函数
连接集合元素:
val list = listOf("Hello", "Kotlin", "World")
val message = list.joinToString(", ") // Hello, Kotlin, World
val message2 = list.joinToString(prefix = "[", postfix = "]") // [Hello, Kotlin, World]
6. 多行字符串
使用三引号""":
val text = """
Line 1
Line 2
Line 3
""".trimIndent()
// 或者使用trimMargin
val text2 = """
|Line 1
|Line 2
|Line 3
""".trimMargin("|")
7. 性能对比
字符串模板 vs 字符串连接
- 字符串模板:编译时优化,性能好
- 字符串连接(
+):每次拼接创建新对象,性能较差
StringBuilder vs buildString
- 两者性能相近
buildString更简洁,推荐使用
8. 使用场景
字符串模板(推荐)
// 简单拼接
val name = "Kotlin"
val greeting = "Hello, $name"
StringBuilder/buildString(大量拼接)
// 循环拼接
val builder = buildString {
for (i in 1..100) {
append(i)
}
}
joinToString(集合拼接)
val list = listOf(1, 2, 3, 4, 5)
val result = list.joinToString("-") // 1-2-3-4-5
9. 最佳实践
- 优先使用字符串模板:简洁、性能好
- 大量拼接使用StringBuilder:避免性能问题
- 集合拼接使用joinToString:代码更清晰
- 避免在循环中使用
+:性能差
10. 示例
// 方式1:字符串模板
val name = "Kotlin"
val message1 = "Hello, $name"
// 方式2:字符串连接
val message2 = "Hello, " + name
// 方式3:buildString
val message3 = buildString {
append("Hello")
append(", ")
append(name)
}
// 方式4:joinToString
val words = listOf("Hello", "Kotlin")
val message4 = words.joinToString(", ")
// 方式5:StringBuilder
val message5 = StringBuilder()
.append("Hello")
.append(", ")
.append(name)
.toString()
1.13 Kotlin的字符串比较方法有哪些?
答案:
Kotlin提供了多种字符串比较方法:
1. == 和 !=(推荐)
Kotlin的==运算符比较字符串内容(等价于Java的equals()):
val str1 = "Kotlin"
val str2 = "Kotlin"
val str3 = "Java"
println(str1 == str2) // true(内容相同)
println(str1 == str3) // false(内容不同)
println(str1 != str3) // true
2. equals()方法
使用equals()方法,可以指定是否忽略大小写:
val str1 = "Kotlin"
val str2 = "kotlin"
val str3 = "Kotlin"
// 区分大小写(默认)
println(str1.equals(str2)) // false
println(str1.equals(str3)) // true
// 忽略大小写
println(str1.equals(str2, ignoreCase = true)) // true
3. compareTo()方法
比较字符串的字典序,返回整数:
val str1 = "apple"
val str2 = "banana"
println(str1.compareTo(str2)) // 负数(apple < banana)
println(str2.compareTo(str1)) // 正数(banana > apple)
println(str1.compareTo("apple")) // 0(相等)
// 忽略大小写
println("Apple".compareTo("apple", ignoreCase = true)) // 0
4. 前缀和后缀比较
startsWith()
val str = "Kotlin Programming"
println(str.startsWith("Kotlin")) // true
println(str.startsWith("Java")) // false
println(str.startsWith("kotlin", ignoreCase = true)) // true
endsWith()
val str = "Kotlin Programming"
println(str.endsWith("Programming")) // true
println(str.endsWith("Kotlin")) // false
5. 包含关系
contains()
val str = "Kotlin Programming"
println(str.contains("Kotlin")) // true
println(str.contains("Java")) // false
println(str.contains("kotlin", ignoreCase = true)) // true
6. 空字符串检查
val str1 = ""
val str2 = " "
val str3 = "Kotlin"
println(str1.isEmpty()) // true
println(str2.isEmpty()) // false(有空格)
println(str2.isBlank()) // true(空白字符串)
println(str3.isNotEmpty()) // true
7. 与Java对比
| Kotlin | Java等价 |
|---|---|
str1 == str2 | str1.equals(str2) |
str1 != str2 | !str1.equals(str2) |
str1 === str2 | str1 == str2(引用比较) |
str1.equals(str2, ignoreCase = true) | str1.equalsIgnoreCase(str2) |
8. 引用比较(===)
===比较引用(地址),类似Java的==:
val str1 = "Kotlin"
val str2 = "Kotlin"
val str3 = StringBuilder("Kotlin").toString()
println(str1 == str2) // true(内容相同)
println(str1 === str2) // true(字符串常量池,同一对象)
println(str1 == str3) // true(内容相同)
println(str1 === str3) // false(不同对象)
9. 常见用法
条件判断
val input = readLine()
if (input == "yes") {
println("Confirmed")
}
忽略大小写比较
val answer = "Yes"
if (answer.equals("yes", ignoreCase = true)) {
println("Confirmed")
}
字符串排序
val list = listOf("banana", "apple", "cherry")
val sorted = list.sorted() // 使用compareTo排序
10. 最佳实践
- 优先使用
==:简洁、易读 - 忽略大小写使用equals():参数清晰
- 排序使用compareTo():返回整数便于排序
- 空检查使用isEmpty()/isBlank():语义清晰
4. 函数基础
1.14 Kotlin的函数声明方式是什么?
答案:
Kotlin有多种函数声明方式:
1. 基本函数声明
// 完整语法
fun functionName(parameter: Type): ReturnType {
// 函数体
return value
}
// 示例
fun greet(name: String): String {
return "Hello, $name"
}
2. 单表达式函数
当函数只有一个表达式时,可以简化:
// 普通函数
fun add(a: Int, b: Int): Int {
return a + b
}
// 单表达式函数(等价)
fun add(a: Int, b: Int): Int = a + b
// 类型推断(可以省略返回类型)
fun add(a: Int, b: Int) = a + b
3. 无返回值函数
使用Unit类型(类似Java的void),可以省略:
// 显式Unit
fun printMessage(message: String): Unit {
println(message)
}
// 省略Unit(等价)
fun printMessage(message: String) {
println(message)
}
4. 顶层函数
函数可以在文件顶层声明,不需要类:
// 文件顶层
fun greet(name: String): String {
return "Hello, $name"
}
fun main() {
println(greet("Kotlin"))
}
5. 成员函数
函数在类中声明:
class Calculator {
fun add(a: Int, b: Int): Int {
return a + b
}
}
// 调用
val calc = Calculator()
val result = calc.add(1, 2)
6. 扩展函数
为现有类添加函数:
fun String.removeSpaces(): String {
return this.replace(" ", "")
}
// 调用
val text = "Hello World"
val result = text.removeSpaces() // "HelloWorld"
7. 高阶函数
函数作为参数或返回值:
// 函数作为参数
fun process(list: List<Int>, operation: (Int) -> Int): List<Int> {
return list.map(operation)
}
// 调用
val numbers = listOf(1, 2, 3)
val doubled = process(numbers) { it * 2 }
8. 内联函数
使用inline关键字:
inline fun <T> measureTime(block: () -> T): T {
val start = System.currentTimeMillis()
val result = block()
val end = System.currentTimeMillis()
println("Time: ${end - start}ms")
return result
}
9. 函数类型
函数也可以作为类型:
// 函数类型
val operation: (Int, Int) -> Int = { a, b -> a + b }
// 调用
val result = operation(1, 2) // 3
10. 与Java对比
| Kotlin | Java等价 |
|---|---|
fun add(a: Int, b: Int): Int | int add(int a, int b) |
fun print(): Unit | void print() |
| 顶层函数 | 静态方法或工具类 |
| 扩展函数 | 工具类静态方法 |
| 单表达式函数 | 需要return语句 |
1.15 Kotlin的函数参数有哪些特性?
答案:
Kotlin的函数参数有以下特性:
1. 默认参数
参数可以有默认值:
fun greet(name: String, greeting: String = "Hello") {
println("$greeting, $name")
}
// 调用
greet("Kotlin") // Hello, Kotlin
greet("Kotlin", "Hi") // Hi, Kotlin
2. 命名参数
调用函数时可以指定参数名:
fun createUser(name: String, age: Int, email: String = "") {
// ...
}
// 调用
createUser("Kotlin", 25, "kotlin@example.com")
createUser(name = "Kotlin", age = 25)
createUser(age = 25, name = "Kotlin") // 可以改变顺序
3. 可变参数(vararg)
使用vararg关键字声明可变参数:
fun sum(vararg numbers: Int): Int {
return numbers.sum()
}
// 调用
sum(1, 2, 3) // 6
sum(1, 2, 3, 4, 5) // 15
// 展开数组
val array = intArrayOf(1, 2, 3)
sum(*array) // 使用*展开
4. 扩展函数参数
fun String.remove(removeChars: CharSequence): String {
var result = this
for (char in removeChars) {
result = result.replace(char.toString(), "")
}
return result
}
// 调用
val text = "Hello World"
val result = text.remove("l") // Heo Word
5. 类型推断参数
Lambda参数可以类型推断:
fun process(list: List<Int>, operation: (Int) -> Int): List<Int> {
return list.map(operation)
}
// 完整类型
process(listOf(1, 2, 3), { x: Int -> x * 2 })
// 类型推断
process(listOf(1, 2, 3)) { it * 2 }
6. 参数验证
参数可以添加条件检查:
fun divide(a: Int, b: Int): Int {
require(b != 0) { "Division by zero" }
return a / b
}
7. 函数参数作为默认值
fun process(
data: String,
transform: (String) -> String = { it.uppercase() }
): String {
return transform(data)
}
// 使用默认转换
process("hello") // "HELLO"
// 自定义转换
process("hello") { it.reversed() } // "olleh"
8. 尾随Lambda
当最后一个参数是函数时,可以写在括号外:
fun process(list: List<Int>, operation: (Int) -> Int): List<Int> {
return list.map(operation)
}
// 两种调用方式等价
process(listOf(1, 2, 3), { it * 2 })
process(listOf(1, 2, 3)) { it * 2 } // 尾随Lambda
9. 参数解构
fun printCoordinates(pair: Pair<Int, Int>) {
val (x, y) = pair
println("x: $x, y: $y")
}
// 或者直接解构
fun printCoordinates((x, y): Pair<Int, Int>) {
println("x: $x, y: $y")
}
10. 与Java对比
| Kotlin | Java |
|---|---|
| 默认参数 | 需要方法重载 |
| 命名参数 | 不支持 |
vararg | ...(可变参数) |
| 尾随Lambda | 需要接口或函数式接口 |
1.16 Kotlin的默认参数是什么?
答案:
默认参数(Default Parameters)允许函数参数有默认值,调用时可以省略该参数。
1. 基本语法
fun greet(name: String, greeting: String = "Hello") {
println("$greeting, $name")
}
// 调用
greet("Kotlin") // Hello, Kotlin(使用默认值)
greet("Kotlin", "Hi") // Hi, Kotlin(覆盖默认值)
2. 多个默认参数
fun createUser(
name: String,
age: Int = 0,
email: String = "",
active: Boolean = true
) {
println("Name: $name, Age: $age, Email: $email, Active: $active")
}
// 调用
createUser("Kotlin") // 使用所有默认值
createUser("Kotlin", 25) // 指定age
createUser("Kotlin", 25, "kotlin@example.com") // 指定age和email
createUser("Kotlin", 25, "kotlin@example.com", false) // 指定所有参数
3. 命名参数配合默认参数
fun createUser(
name: String,
age: Int = 0,
email: String = "",
active: Boolean = true
) {
// ...
}
// 调用时可以跳过某些参数
createUser("Kotlin", active = false) // 只指定name和active
createUser("Kotlin", email = "kotlin@example.com") // 只指定name和email
4. 扩展函数中的默认参数
fun String.remove(
chars: String,
caseSensitive: Boolean = true
): String {
var result = this
for (char in chars) {
val pattern = if (caseSensitive) char.toString() else char.toString().lowercase()
result = result.replace(pattern, "", ignoreCase = !caseSensitive)
}
return result
}
// 调用
"Hello".remove("l") // "Heo"
"Hello".remove("l", caseSensitive = false) // "Heo"
5. 与Java对比
Java不支持默认参数,需要使用方法重载:
// Kotlin(一个函数)
fun greet(name: String, greeting: String = "Hello") {
println("$greeting, $name")
}
// Java等价(需要多个重载方法)
public void greet(String name) {
greet(name, "Hello");
}
public void greet(String name, String greeting) {
System.out.println(greeting + ", " + name);
}
6. 默认参数的优点
- 减少方法重载:一个函数代替多个重载
- 代码简洁:避免重复代码
- 灵活性:调用时可以选择性指定参数
- 向后兼容:添加默认参数不影响现有代码
7. 注意事项
- 默认参数在调用处展开:不是编译时展开
- 命名参数提高可读性:特别是跳过某些参数时
- 合理使用:避免参数过多(建议不超过3-4个)
8. 实际应用
Builder模式替代
// 使用默认参数
data class User(
val name: String,
val age: Int = 0,
val email: String = "",
val active: Boolean = true
)
val user = User("Kotlin", age = 25)
API设计
fun log(
message: String,
level: LogLevel = LogLevel.INFO,
timestamp: Long = System.currentTimeMillis()
) {
// 日志记录
}
// 调用
log("User logged in")
log("Error occurred", LogLevel.ERROR)
1.17 Kotlin的命名参数是什么?
答案:
命名参数(Named Parameters)允许在调用函数时指定参数名,提高代码可读性和灵活性。
1. 基本语法
fun createUser(name: String, age: Int, email: String) {
println("Name: $name, Age: $age, Email: $email")
}
// 位置参数调用
createUser("Kotlin", 25, "kotlin@example.com")
// 命名参数调用
createUser(name = "Kotlin", age = 25, email = "kotlin@example.com")
2. 参数顺序可以改变
使用命名参数时,可以改变参数顺序:
fun createUser(name: String, age: Int, email: String) {
// ...
}
// 位置参数(必须按顺序)
createUser("Kotlin", 25, "kotlin@example.com")
// 命名参数(可以改变顺序)
createUser(age = 25, name = "Kotlin", email = "kotlin@example.com")
3. 混合使用
可以混合使用位置参数和命名参数(位置参数必须在前面):
fun createUser(name: String, age: Int, email: String, active: Boolean) {
// ...
}
// 混合使用
createUser("Kotlin", 25, email = "kotlin@example.com", active = true)
// createUser(name = "Kotlin", 25, "kotlin@example.com", true) // ❌ 错误,命名参数后不能跟位置参数
4. 配合默认参数
命名参数与默认参数配合使用,可以跳过某些参数:
fun createUser(
name: String,
age: Int = 0,
email: String = "",
active: Boolean = true
) {
// ...
}
// 调用
createUser("Kotlin") // 所有默认值
createUser("Kotlin", age = 25) // 只指定age
createUser("Kotlin", email = "kotlin@example.com") // 只指定email
createUser("Kotlin", active = false, age = 30) // 改变顺序
5. 提高可读性
命名参数使代码更易读,特别是Boolean参数:
// 不使用命名参数(不清晰)
process("data", true, false) // true和false的含义不清楚
// 使用命名参数(清晰)
process(
data = "data",
async = true,
cache = false
)
6. 实际应用
配置对象
fun createConnection(
host: String,
port: Int = 80,
timeout: Int = 5000,
ssl: Boolean = false
) {
// 创建连接
}
// 调用
createConnection(
host = "example.com",
port = 443,
ssl = true
)
函数调用
fun formatDate(
year: Int,
month: Int,
day: Int,
separator: String = "-"
): String {
return "$year$separator$month$separator$day"
}
// 调用
formatDate(year = 2024, month = 1, day = 1) // 2024-1-1
formatDate(2024, 1, 1, separator = "/") // 2024/1/1
7. 与Java对比
Java不支持命名参数,需要通过Builder模式或其他方式实现类似效果:
// Kotlin
createUser(name = "Kotlin", age = 25)
// Java需要使用Builder模式
new UserBuilder()
.setName("Java")
.setAge(25)
.build();
8. 最佳实践
- Boolean参数使用命名参数:提高可读性
- 多个参数时使用命名参数:避免混淆
- 配合默认参数使用:灵活性更好
- API设计时考虑:提高用户体验
1.18 Kotlin的单表达式函数是什么?
答案:
单表达式函数(Single Expression Functions)是Kotlin中函数体只有一个表达式时的简化写法。
1. 基本语法
// 普通函数
fun add(a: Int, b: Int): Int {
return a + b
}
// 单表达式函数(等价)
fun add(a: Int, b: Int): Int = a + b
// 类型推断(可以省略返回类型)
fun add(a: Int, b: Int) = a + b
2. 类型推断
单表达式函数支持类型推断,可以省略返回类型:
// 显式返回类型
fun multiply(a: Int, b: Int): Int = a * b
// 类型推断(等价)
fun multiply(a: Int, b: Int) = a * b
// 复杂类型也可以推断
fun getNames() = listOf("Alice", "Bob", "Charlie")
// 推断为 List<String>
3. 各种使用场景
简单计算
fun square(x: Int) = x * x
fun max(a: Int, b: Int) = if (a > b) a else b
属性访问
class User(val name: String, val age: Int) {
fun isAdult() = age >= 18
fun displayName() = name.uppercase()
}
集合操作
fun getFirstItem(list: List<Int>) = list.first()
fun getLastItem(list: List<Int>) = list.last()
字符串操作
fun String.removeSpaces() = this.replace(" ", "")
fun String.isNotEmpty() = length > 0
4. 与Lambda的区别
单表达式函数是函数的简化写法,Lambda是函数对象:
// 单表达式函数
fun square(x: Int) = x * x
// Lambda(函数对象)
val square = { x: Int -> x * x }
// 调用
square(5) // 两种方式都可以
5. 优势
- 代码简洁:减少冗余代码
- 类型推断:自动推断返回类型
- 可读性:简单函数更清晰
- 一致性:与Lambda表达式风格一致
6. 注意事项
- 仅限单表达式:多个表达式需要普通函数
- 复杂逻辑不适用:可读性会降低
- 类型推断有限制:复杂类型建议显式声明
7. 实际应用
Getter方法
class Rectangle(val width: Int, val height: Int) {
fun area() = width * height
fun perimeter() = 2 * (width + height)
fun isSquare() = width == height
}
工具函数
fun String.isEmail() = this.contains("@") && this.contains(".")
fun List<Int>.sum() = this.fold(0) { acc, x -> acc + x }
工厂函数
fun createUser(name: String, age: Int) = User(name, age)
fun createPoint(x: Int, y: Int) = Point(x, y)
8. 与Java对比
// Kotlin单表达式函数
fun add(a: Int, b: Int) = a + b
// Java等价
public int add(int a, int b) {
return a + b;
}
9. 最佳实践
- 简单函数使用:提高代码简洁性
- Getter方法:替代Java的getter
- 工具函数:简单转换和计算
- 避免过度使用:复杂逻辑使用普通函数
1.19 Kotlin的扩展函数是什么?
答案:
扩展函数(Extension Functions)允许在不修改原有类的情况下,为类添加新函数。
1. 基本语法
// 为String类添加扩展函数
fun String.removeSpaces(): String {
return this.replace(" ", "")
}
// 调用
val text = "Hello World"
val result = text.removeSpaces() // "HelloWorld"
2. 语法说明
fun ReceiverType.functionName(parameters): ReturnType {
// 函数体
// this指向接收者对象
}
ReceiverType:被扩展的类型this:指向接收者对象(可以省略)
3. 示例
// 为Int添加扩展函数
fun Int.square(): Int = this * this
// 调用
val result = 5.square() // 25
// 为List添加扩展函数
fun <T> List<T>.second(): T? = if (size >= 2) this[1] else null
// 调用
val list = listOf(1, 2, 3)
val second = list.second() // 2
4. 实现原理
扩展函数是静态方法,编译时转换为静态方法调用:
// Kotlin代码
fun String.removeSpaces(): String = this.replace(" ", "")
// 编译后(伪代码)
public static String removeSpaces(String $this) {
return $this.replace(" ", "");
}
5. 与成员函数的区别
| 特性 | 扩展函数 | 成员函数 |
|---|---|---|
| 定义位置 | 类外部 | 类内部 |
| 优先级 | 低(成员函数优先) | 高 |
| 可见性 | 需要导入 | 类作用域 |
| 多态性 | 不支持(静态解析) | 支持(动态解析) |
6. 扩展函数的优势
- 不修改原类:不需要修改原始类
- 代码组织:可以按功能组织代码
- API扩展:为第三方库添加功能
- 语法简洁:调用方式更自然
7. 实际应用
字符串操作
fun String.isEmail(): Boolean {
return this.contains("@") && this.contains(".")
}
fun String.capitalizeFirst(): String {
return if (isEmpty()) this
else this[0].uppercase() + substring(1)
}
集合操作
fun <T> List<T>.secondOrNull(): T? = if (size >= 2) this[1] else null
fun <T> List<T>.penultimate(): T? = if (size >= 2) this[size - 2] else null
工具函数
fun Int.isEven() = this % 2 == 0
fun Int.isOdd() = this % 2 != 0
fun Double.toPercentage() = "${this * 100}%"
8. 扩展属性的限制
扩展函数可以有属性,但不能存储状态:
// 扩展属性(只能有getter)
val String.lastChar: Char
get() = this[length - 1]
// 可变扩展属性(需要vararg)
var StringBuilder.lastChar: Char
get() = get(length - 1)
set(value) = setCharAt(length - 1, value)
9. 与Java对比
// Kotlin扩展函数
fun String.removeSpaces(): String = this.replace(" ", "")
// Java等价(工具类)
public class StringUtils {
public static String removeSpaces(String str) {
return str.replace(" ", "");
}
}
10. 最佳实践
- 合理使用:不要过度使用扩展函数
- 命名清晰:使用有意义的函数名
- 避免冲突:注意与成员函数的优先级
- 文档说明:为扩展函数添加文档注释
1.20 Kotlin的Lambda表达式是什么?
答案:
Lambda表达式(Lambda Expression)是Kotlin中用于定义匿名函数的简洁语法,是函数式编程的基础。
1. 基本语法
// 基本语法
{ parameters -> body }
// 示例
val add = { x: Int, y: Int -> x + y }
// 调用
val result = add(1, 2) // 3
2. 简化写法
类型推断
// 完整类型
val square: (Int) -> Int = { x: Int -> x * x }
// 类型推断(可以省略参数类型)
val square = { x: Int -> x * x }
// 在函数参数中使用(可以进一步简化)
list.map { x: Int -> x * 2 }
list.map { x -> x * 2 } // 类型推断
list.map { it * 2 } // it代表单个参数
3. it关键字
当Lambda只有一个参数时,可以使用it关键字:
// 单个参数
val numbers = listOf(1, 2, 3)
// 完整写法
numbers.map { x -> x * 2 }
// 简化写法(it)
numbers.map { it * 2 }
4. Lambda类型
Lambda表达式的类型是函数类型:
// 无参无返回值
val action: () -> Unit = { println("Hello") }
// 一个参数,返回Int
val square: (Int) -> Int = { it * it }
// 两个参数,返回Boolean
val compare: (Int, Int) -> Boolean = { a, b -> a > b }
// 带Unit返回值的Lambda
val print: (String) -> Unit = { println(it) }
5. 高阶函数中使用
Lambda表达式常用于高阶函数:
// filter:过滤元素
val numbers = listOf(1, 2, 3, 4, 5)
val evens = numbers.filter { it % 2 == 0 } // [2, 4]
// map:转换元素
val doubled = numbers.map { it * 2 } // [2, 4, 6, 8, 10]
// forEach:遍历元素
numbers.forEach { println(it) }
6. 尾随Lambda
当Lambda是函数的最后一个参数时,可以写在括号外:
// 普通调用
process(listOf(1, 2, 3), { it * 2 })
// 尾随Lambda
process(listOf(1, 2, 3)) { it * 2 }
7. 捕获变量
Lambda可以访问外部变量:
var count = 0
val increment = { count++ }
increment() // count = 1
increment() // count = 2
// 在Lambda中修改外部变量
val numbers = listOf(1, 2, 3)
var sum = 0
numbers.forEach { sum += it }
println(sum) // 6
8. 与匿名函数对比
// Lambda表达式
val square = { x: Int -> x * x }
// 匿名函数(等价)
val square2 = fun(x: Int): Int {
return x * x
}
9. 实际应用
集合操作
val numbers = listOf(1, 2, 3, 4, 5)
// 过滤
val evens = numbers.filter { it % 2 == 0 }
// 映射
val squares = numbers.map { it * it }
// 归约
val sum = numbers.reduce { acc, x -> acc + x }
// 排序
val sorted = numbers.sortedByDescending { it }
事件处理
button.setOnClickListener { view ->
// 处理点击事件
println("Button clicked")
}
线程处理
Thread {
// 后台任务
val result = processData()
// 更新UI(在主线程)
runOnUiThread {
updateUI(result)
}
}.start()
10. 与Java对比
// Kotlin Lambda
list.filter { it > 0 }.map { it * 2 }
// Java 8+ Lambda
list.stream()
.filter(x -> x > 0)
.map(x -> x * 2)
.collect(Collectors.toList());
11. 最佳实践
- 类型推断:充分利用类型推断
- it关键字:单个参数时使用it
- 尾随Lambda:提高可读性
- 避免过长:复杂逻辑使用命名函数
1.21 Lambda表达式的语法和用法
答案:
Lambda表达式的详细语法和用法:
1. 完整语法
{ parameter1: Type1, parameter2: Type2 -> body }
{}:Lambda表达式的括号parameter:参数列表Type:参数类型(可省略,类型推断)->:分隔符body:函数体(表达式或语句块)
2. 无参Lambda
// 无参Lambda
val action = { println("Hello") }
// 调用
action()
// 类型
val action: () -> Unit = { println("Hello") }
3. 单参Lambda
// 完整写法
val square = { x: Int -> x * x }
// 使用it
val square = { it * it } // 需要在上下文中推断类型
// 调用
square(5) // 25
4. 多参Lambda
// 两个参数
val add = { x: Int, y: Int -> x + y }
// 调用
add(1, 2) // 3
// 类型
val compare: (Int, Int) -> Boolean = { a, b -> a > b }
5. 类型推断
Lambda表达式支持类型推断:
// 显式类型
val numbers = listOf(1, 2, 3)
numbers.map { x: Int -> x * 2 }
// 类型推断
numbers.map { x -> x * 2 }
// it简化
numbers.map { it * 2 }
6. Lambda体
Lambda体可以是表达式或语句块:
表达式(单行)
val square = { x: Int -> x * x }
语句块(多行)
val process = { x: Int ->
val doubled = x * 2
println("Processed: $doubled")
doubled
}
7. 返回值
Lambda的返回值是最后一条表达式的值:
// 返回值
val add = { x: Int, y: Int -> x + y } // 返回Int
// 显式return(仅用于命名函数,Lambda中使用标签)
val process = { x: Int ->
if (x < 0) return@process 0 // 使用标签return
x * 2
}
8. 高阶函数中使用
// filter
val numbers = listOf(1, 2, 3, 4, 5)
val evens = numbers.filter { it % 2 == 0 }
// map
val squares = numbers.map { it * it }
// reduce
val sum = numbers.reduce { acc, x -> acc + x }
// forEach
numbers.forEach { println(it) }
9. 尾随Lambda
// 普通调用
process(listOf(1, 2, 3), { it * 2 })
// 尾随Lambda
process(listOf(1, 2, 3)) { it * 2 }
// 多个Lambda
process(data) { it * 2 } { println(it) }
10. 常用模式
过滤
list.filter { it > 0 }
list.filterNot { it < 0 }
list.filterIndexed { index, value -> index % 2 == 0 }
映射
list.map { it * 2 }
list.mapIndexed { index, value -> index * value }
list.mapNotNull { if (it > 0) it * 2 else null }
查找
list.find { it > 5 }
list.firstOrNull { it > 5 }
list.lastOrNull { it < 10 }
11. 最佳实践
- 利用类型推断:减少冗余类型声明
- 使用it:单参数时使用it
- 合理命名:多参数时使用有意义的参数名
- 避免过长:复杂逻辑提取为命名函数
1.22 Lambda表达式的类型推断
答案:
Kotlin编译器可以根据上下文自动推断Lambda表达式的类型。
1. 基本类型推断
// 显式类型
val square: (Int) -> Int = { x: Int -> x * 2 }
// 类型推断(可以省略参数类型)
val square: (Int) -> Int = { x -> x * 2 }
// 完整推断(在函数参数中)
list.map { x: Int -> x * 2 } // 可以推断
list.map { x -> x * 2 } // 也可以推断
list.map { it * 2 } // 使用it,完全推断
2. 在函数参数中推断
// 函数定义
fun process(list: List<Int>, operation: (Int) -> Int): List<Int> {
return list.map(operation)
}
// 调用时推断
process(listOf(1, 2, 3)) { it * 2 } // 自动推断it为Int
3. 集合操作中的推断
val numbers = listOf(1, 2, 3, 4, 5)
// filter:推断it为Int
val evens = numbers.filter { it % 2 == 0 }
// map:推断it为Int,返回类型为Int
val doubled = numbers.map { it * 2 }
// map:推断返回类型为String
val strings = numbers.map { it.toString() }
4. 多参数推断
// 两个参数
val add = { x: Int, y: Int -> x + y }
// 在reduce中使用推断
val numbers = listOf(1, 2, 3)
val sum = numbers.reduce { acc, x -> acc + x } // 推断acc和x为Int
5. 类型推断的限制
某些情况下需要显式类型:
复杂类型
// 需要显式类型
val complex: (List<Int>) -> List<String> = { list ->
list.map { it.toString() }
}
递归Lambda
// 递归需要显式类型
val factorial: (Int) -> Int = { n ->
if (n <= 1) 1 else n * factorial(n - 1)
}
6. 类型推断的优势
- 代码简洁:减少冗余类型声明
- 易于重构:修改类型时自动更新
- 减少错误:编译器自动检查类型
7. 推断示例
// 示例1:简单推断
val numbers = listOf(1, 2, 3)
val doubled = numbers.map { it * 2 } // 推断it为Int
// 示例2:多参数推断
val pairs = listOf(1 to "a", 2 to "b")
val keys = pairs.map { (key, value) -> key } // 推断key为Int
// 示例3:嵌套推断
val matrix = listOf(listOf(1, 2), listOf(3, 4))
val flattened = matrix.flatMap { it.map { it * 2 } } // 多层推断
1.23 Lambda表达式的参数简化
答案:
Kotlin提供了多种方式简化Lambda表达式的参数。
1. it关键字
当Lambda只有一个参数时,可以使用it关键字:
// 完整写法
list.map { x -> x * 2 }
// 使用it(简化)
list.map { it * 2 }
// 多个参数不能使用it
list.reduce { acc, x -> acc + x } // 必须命名参数
2. 解构参数
当参数是Pair或数据类时,可以解构:
// Pair解构
val pairs = listOf(1 to "a", 2 to "b")
pairs.map { (key, value) -> "$key: $value" }
// 数据类解构
data class Point(val x: Int, val y: Int)
val points = listOf(Point(1, 2), Point(3, 4))
points.map { (x, y) -> x + y }
3. 未使用的参数
使用_忽略未使用的参数:
// 忽略参数
map.forEach { _, value -> println(value) }
// 忽略所有参数
listOf(1, 2, 3).forEach { _ -> println("Item") }
4. 参数类型省略
在可以推断的情况下,可以省略参数类型:
// 显式类型
list.map { x: Int -> x * 2 }
// 类型推断(省略类型)
list.map { x -> x * 2 }
// 使用it(完全省略)
list.map { it * 2 }
5. 简化示例
// 示例1:单参数简化
list.filter { it > 0 }
list.map { it.toString() }
// 示例2:Pair解构
map.forEach { (key, value) -> println("$key: $value") }
// 示例3:忽略参数
list.forEach { _ -> println("Processing") }
6. 最佳实践
- 使用it:单参数时优先使用it
- 有意义命名:多参数时使用有意义的参数名
- 解构参数:Pair或数据类使用解构
- 忽略未使用:使用_忽略未使用的参数
1.24 Lambda表达式的返回值
答案:
Lambda表达式的返回值规则:
1. 表达式返回值
Lambda的返回值是最后一条表达式的值:
// 表达式Lambda(返回值)
val add = { x: Int, y: Int -> x + y } // 返回Int
val square = { x: Int -> x * x } // 返回Int
2. 语句块返回值
语句块中,返回值是最后一条表达式的值:
val process = { x: Int ->
val doubled = x * 2
println("Processed: $doubled")
doubled // 这是返回值
}
3. Unit返回
如果没有返回值,返回Unit:
val print = { x: Int -> println(x) } // 返回Unit
val action = { println("Hello") } // 返回Unit
4. 条件返回值
val max = { a: Int, b: Int ->
if (a > b) a else b // 返回Int
}
5. 早期返回
Lambda中使用标签return:
val process = list@ { x: Int ->
if (x < 0) return@list 0 // 使用标签return
x * 2
}
// 或者使用run
val process = { x: Int ->
run {
if (x < 0) return@run 0
x * 2
}
}
6. 返回类型推断
// 推断返回类型
val square = { x: Int -> x * x } // 推断为(Int) -> Int
// 在map中使用
list.map { it * 2 } // 推断返回List<Int>
7. 示例
// 返回Int
val sum = { a: Int, b: Int -> a + b }
// 返回String
val format = { x: Int -> "Value: $x" }
// 返回Boolean
val isEven = { x: Int -> x % 2 == 0 }
// 返回Unit
val print = { x: Int -> println(x) }
1.25 Kotlin的高阶函数是什么?
答案:
高阶函数(Higher-Order Functions)是接受函数作为参数或返回函数的函数。
1. 基本概念
高阶函数是函数式编程的核心概念,Kotlin原生支持:
// 函数作为参数
fun process(list: List<Int>, operation: (Int) -> Int): List<Int> {
return list.map(operation)
}
// 函数作为返回值
fun getOperation(): (Int) -> Int {
return { it * 2 }
}
2. 函数作为参数
最常见的用法是函数作为参数:
// 定义高阶函数
fun filter(numbers: List<Int>, predicate: (Int) -> Boolean): List<Int> {
return numbers.filter(predicate)
}
// 调用
val numbers = listOf(1, 2, 3, 4, 5)
val evens = filter(numbers) { it % 2 == 0 } // [2, 4]
3. 函数作为返回值
函数可以作为返回值:
// 函数作为返回值
fun getMultiplier(factor: Int): (Int) -> Int {
return { it * factor }
}
// 调用
val double = getMultiplier(2)
val triple = getMultiplier(3)
double(5) // 10
triple(5) // 15
4. 集合操作中的高阶函数
Kotlin标准库提供了丰富的高阶函数:
val numbers = listOf(1, 2, 3, 4, 5)
// filter:过滤
val evens = numbers.filter { it % 2 == 0 }
// map:转换
val doubled = numbers.map { it * 2 }
// reduce:归约
val sum = numbers.reduce { acc, x -> acc + x }
// forEach:遍历
numbers.forEach { println(it) }
5. 自定义高阶函数
// 简单的高阶函数
fun <T> List<T>.myFilter(predicate: (T) -> Boolean): List<T> {
val result = mutableListOf<T>()
for (item in this) {
if (predicate(item)) {
result.add(item)
}
}
return result
}
// 使用
val numbers = listOf(1, 2, 3, 4, 5)
val evens = numbers.myFilter { it % 2 == 0 }
6. 高阶函数的优势
- 代码复用:抽象通用模式
- 灵活性强:可以动态改变行为
- 表达力强:代码更简洁
- 函数式编程:支持函数式编程风格
7. 实际应用
事件处理
fun onButtonClick(handler: () -> Unit) {
// 注册点击事件
button.setOnClickListener { handler() }
}
异步处理
fun executeAsync(task: () -> Unit, callback: (Boolean) -> Unit) {
Thread {
try {
task()
callback(true)
} catch (e: Exception) {
callback(false)
}
}.start()
}
数据转换
fun transform(data: List<String>, transformer: (String) -> String): List<String> {
return data.map(transformer)
}
// 使用
val names = listOf("alice", "bob")
val uppercased = transform(names) { it.uppercase() }
8. 与Java对比
// Kotlin高阶函数
fun process(list: List<Int>, operation: (Int) -> Int): List<Int> {
return list.map(operation)
}
// Java等价(需要接口)
public interface Operation {
int apply(int x);
}
public List<Integer> process(List<Integer> list, Operation operation) {
return list.stream()
.map(operation::apply)
.collect(Collectors.toList());
}
9. 最佳实践
- 合理使用:不要过度使用高阶函数
- 命名清晰:函数参数使用有意义的名称
- 类型明确:复杂类型建议显式声明
- 性能考虑:注意Lambda的性能开销(可以使用内联函数优化)
1.26 高阶函数的定义和特点是什么?
答案:
高阶函数的定义和特点:
1. 定义
高阶函数是接受函数作为参数或返回函数的函数。
2. 特点
接受函数参数
fun process(list: List<Int>, operation: (Int) -> Int): List<Int> {
return list.map(operation)
}
返回函数
fun getOperation(): (Int) -> Int {
return { it * 2 }
}
两者结合
fun createOperation(factor: Int): (Int) -> Int {
return { it * factor }
}
3. 类型系统
高阶函数使用函数类型:
// 函数类型语法
(参数类型) -> 返回类型
// 示例
(Int) -> Int // 接受Int,返回Int
(Int, String) -> Boolean // 接受Int和String,返回Boolean
() -> Unit // 无参数,返回Unit
(String) -> String? // 接受String,返回可空String
4. 函数类型特性
- 类型安全:编译时检查类型
- 可空性:函数类型可以可空
- 泛型:支持泛型函数类型
// 可空函数类型
var operation: ((Int) -> Int)? = null
// 泛型函数类型
fun <T> process(list: List<T>, operation: (T) -> T): List<T> {
return list.map(operation)
}
5. Lambda和高阶函数
Lambda表达式常用于高阶函数:
// Lambda作为参数
numbers.filter { it > 0 }
// Lambda作为返回值
fun getFilter(): (Int) -> Boolean {
return { it > 0 }
}
6. 内联优化
可以使用inline关键字优化高阶函数性能:
inline fun <T> process(list: List<T>, operation: (T) -> T): List<T> {
return list.map(operation)
}
7. 实际应用示例
回调函数
fun loadData(onSuccess: (String) -> Unit, onError: (Exception) -> Unit) {
try {
val data = fetchData()
onSuccess(data)
} catch (e: Exception) {
onError(e)
}
}
策略模式
fun calculate(a: Int, b: Int, strategy: (Int, Int) -> Int): Int {
return strategy(a, b)
}
// 使用
calculate(10, 5) { a, b -> a + b } // 加法
calculate(10, 5) { a, b -> a * b } // 乘法
1.27 高阶函数和普通函数的区别是什么?
答案:
高阶函数和普通函数的主要区别:
1. 定义区别
普通函数
fun add(a: Int, b: Int): Int {
return a + b
}
高阶函数
// 接受函数参数
fun process(list: List<Int>, operation: (Int) -> Int): List<Int> {
return list.map(operation)
}
// 返回函数
fun getOperation(): (Int) -> Int {
return { it * 2 }
}
2. 参数类型
| 特性 | 普通函数 | 高阶函数 |
|---|---|---|
| 参数类型 | 基本类型、对象 | 函数类型 |
| 返回值类型 | 基本类型、对象 | 函数类型 |
| 灵活性 | 相对固定 | 高度灵活 |
3. 使用方式
普通函数
fun calculate(a: Int, b: Int): Int = a + b
val result = calculate(1, 2) // 3
高阶函数
fun process(list: List<Int>, operation: (Int) -> Int): List<Int> {
return list.map(operation)
}
val numbers = listOf(1, 2, 3)
val doubled = process(numbers) { it * 2 } // [2, 4, 6]
val squared = process(numbers) { it * it } // [1, 4, 9]
4. 灵活性
高阶函数更灵活,可以动态改变行为:
// 同一个高阶函数,不同行为
numbers.filter { it > 0 } // 正数
numbers.filter { it % 2 == 0 } // 偶数
numbers.filter { it < 10 } // 小于10
5. 抽象程度
- 普通函数:具体的操作
- 高阶函数:抽象的操作模式
6. 性能
- 普通函数:直接调用,性能最优
- 高阶函数:需要传递函数对象,有额外开销(可以使用
inline优化)
7. 适用场景
普通函数适用于:
- 具体的计算或操作
- 简单的逻辑处理
- 性能敏感的场景
高阶函数适用于:
- 需要抽象通用模式
- 需要动态改变行为
- 函数式编程风格
- 代码复用
1.28 高阶函数的类型签名是什么?
答案:
高阶函数的类型签名描述了函数的参数类型和返回类型。
1. 函数类型语法
(参数类型列表) -> 返回类型
2. 基本类型签名
// 无参无返回值
() -> Unit
// 一个参数
(Int) -> Int
// 多个参数
(Int, String) -> Boolean
// 可空类型
(String?) -> String
// 返回可空类型
(Int) -> String?
3. 函数参数类型
接受函数参数的函数类型
// 接受函数参数的高阶函数
fun process(operation: (Int) -> Int): Int {
return operation(5)
}
// operation的类型是 (Int) -> Int
返回函数的高阶函数类型
// 返回函数的高阶函数
fun getOperation(): (Int) -> Int {
return { it * 2 }
}
// 返回类型是 (Int) -> Int
4. 复杂类型签名
// 接受函数,返回函数
fun <T> createTransformer(operation: (T) -> T): (List<T>) -> List<T> {
return { list -> list.map(operation) }
}
// 类型签名:(operation: (T) -> T) -> (List<T>) -> List<T>
5. 泛型函数类型
// 泛型函数类型
fun <T, R> map(list: List<T>, transform: (T) -> R): List<R> {
return list.map(transform)
}
// transform的类型是 (T) -> R
6. 可空函数类型
// 可空函数类型
var operation: ((Int) -> Int)? = null
// 使用
operation?.invoke(5)
operation?.let { it(5) }
7. 类型别名
可以使用类型别名简化复杂类型:
// 定义类型别名
typealias IntOperation = (Int) -> Int
typealias StringPredicate = (String) -> Boolean
// 使用
fun process(operation: IntOperation): Int {
return operation(5)
}
8. 示例
// 示例1:简单类型签名
val square: (Int) -> Int = { it * it }
// 示例2:复杂类型签名
val filter: (List<Int>, (Int) -> Boolean) -> List<Int> = { list, predicate ->
list.filter(predicate)
}
// 示例3:返回函数
val getOperation: () -> (Int) -> Int = {
{ it * 2 }
}
9. 类型推断
在某些情况下可以推断类型:
// 类型推断
val add = { a: Int, b: Int -> a + b } // 推断为 (Int, Int) -> Int
// 在函数参数中推断
fun process(operation: (Int) -> Int) { }
process { it * 2 } // 自动推断it为Int
1.29 高阶函数的使用场景有哪些?
答案:
高阶函数的常见使用场景:
1. 集合操作
Kotlin标准库提供了丰富的高阶函数:
val numbers = listOf(1, 2, 3, 4, 5)
// filter:过滤
val evens = numbers.filter { it % 2 == 0 }
// map:转换
val doubled = numbers.map { it * 2 }
// reduce:归约
val sum = numbers.reduce { acc, x -> acc + x }
// forEach:遍历
numbers.forEach { println(it) }
2. 事件处理
回调函数:
fun onButtonClick(handler: () -> Unit) {
button.setOnClickListener { handler() }
}
// 使用
onButtonClick {
println("Button clicked")
}
3. 异步处理
异步操作的回调:
fun loadData(
onSuccess: (String) -> Unit,
onError: (Exception) -> Unit
) {
Thread {
try {
val data = fetchData()
onSuccess(data)
} catch (e: Exception) {
onError(e)
}
}.start()
}
4. 策略模式
动态改变算法:
fun calculate(a: Int, b: Int, strategy: (Int, Int) -> Int): Int {
return strategy(a, b)
}
// 使用
calculate(10, 5) { a, b -> a + b } // 加法
calculate(10, 5) { a, b -> a * b } // 乘法
5. 模板方法模式
定义算法框架:
fun processData(
data: List<Int>,
preProcess: (List<Int>) -> List<Int>,
transform: (Int) -> Int,
postProcess: (List<Int>) -> List<Int>
): List<Int> {
val preprocessed = preProcess(data)
val transformed = preprocessed.map(transform)
return postProcess(transformed)
}
6. 验证和转换
数据验证和转换:
fun validateAndTransform(
data: String,
validator: (String) -> Boolean,
transformer: (String) -> String
): String? {
return if (validator(data)) {
transformer(data)
} else {
null
}
}
7. 条件执行
根据条件执行不同的操作:
fun conditional(
condition: Boolean,
ifTrue: () -> Unit,
ifFalse: () -> Unit
) {
if (condition) ifTrue() else ifFalse()
}
8. 资源管理
确保资源正确释放:
fun <T> useResource(resource: Resource, action: (Resource) -> T): T {
try {
return action(resource)
} finally {
resource.close()
}
}
9. 重试机制
实现重试逻辑:
fun <T> retry(
times: Int,
operation: () -> T
): T? {
repeat(times) {
try {
return operation()
} catch (e: Exception) {
if (it == times - 1) throw e
}
}
return null
}
10. 装饰器模式
为函数添加额外功能:
fun <T> logExecution(operation: () -> T): T {
println("Starting execution")
val result = operation()
println("Execution completed")
return result
}
1.30 高阶函数和Lambda表达式的关系是什么?
答案:
高阶函数和Lambda表达式密切相关,Lambda表达式是传递函数给高阶函数的主要方式。
1. Lambda作为高阶函数的参数
最常见的情况是Lambda作为高阶函数的参数:
// 高阶函数
fun process(list: List<Int>, operation: (Int) -> Int): List<Int> {
return list.map(operation)
}
// Lambda作为参数
val numbers = listOf(1, 2, 3)
val doubled = process(numbers) { it * 2 } // 使用Lambda
2. Lambda是高阶函数的实现
Lambda表达式是实现高阶函数行为的主要方式:
// 使用Lambda实现不同的行为
numbers.filter { it > 0 } // Lambda实现过滤条件
numbers.map { it * 2 } // Lambda实现转换逻辑
numbers.reduce { acc, x -> acc + x } // Lambda实现归约逻辑
3. 函数类型和Lambda
函数类型定义了Lambda的签名:
// 函数类型:(Int) -> Int
val square: (Int) -> Int = { it * it } // Lambda实现
// 作为参数传递
fun process(operation: (Int) -> Int) { }
process { it * it } // Lambda作为参数
4. 关系总结
- 高阶函数:接受或返回函数的函数
- Lambda表达式:匿名函数的简洁写法
- 关系:Lambda是实现高阶函数行为的主要工具
5. 实际应用
// 高阶函数定义
fun <T> List<T>.myFilter(predicate: (T) -> Boolean): List<T> {
return filter(predicate)
}
// Lambda使用
val numbers = listOf(1, 2, 3, 4, 5)
val evens = numbers.myFilter { it % 2 == 0 } // Lambda实现predicate
1.31 如何定义和使用高阶函数?
答案:
定义和使用高阶函数的步骤:
1. 定义高阶函数
接受函数参数
fun process(list: List<Int>, operation: (Int) -> Int): List<Int> {
return list.map(operation)
}
返回函数
fun getOperation(): (Int) -> Int {
return { it * 2 }
}
两者结合
fun createOperation(factor: Int): (Int) -> Int {
return { it * factor }
}
2. 使用Lambda调用
val numbers = listOf(1, 2, 3)
// Lambda作为参数
val doubled = process(numbers) { it * 2 }
// 命名函数也可以
fun double(x: Int) = x * 2
val doubled2 = process(numbers, ::double) // 使用函数引用
3. 扩展函数作为高阶函数
fun <T> List<T>.myFilter(predicate: (T) -> Boolean): List<T> {
return filter(predicate)
}
// 使用
val numbers = listOf(1, 2, 3, 4, 5)
val evens = numbers.myFilter { it % 2 == 0 }
4. 内联优化
使用inline优化性能:
inline fun <T> process(list: List<T>, operation: (T) -> T): List<T> {
return list.map(operation)
}
5. 实际示例
// 定义
fun <T> transform(
data: T,
transform: (T) -> T
): T {
return transform(data)
}
// 使用
val number = 5
val squared = transform(number) { it * it } // 25
1.32 高阶函数在集合操作中的应用
答案:
Kotlin标准库的集合操作大量使用高阶函数:
1. filter
过滤元素:
val numbers = listOf(1, 2, 3, 4, 5)
val evens = numbers.filter { it % 2 == 0 } // [2, 4]
val positives = numbers.filter { it > 0 } // [1, 2, 3, 4, 5]
2. map
转换元素:
val numbers = listOf(1, 2, 3)
val doubled = numbers.map { it * 2 } // [2, 4, 6]
val strings = numbers.map { it.toString() } // ["1", "2", "3"]
3. reduce/fold
归约操作:
val numbers = listOf(1, 2, 3, 4, 5)
val sum = numbers.reduce { acc, x -> acc + x } // 15
val product = numbers.fold(1) { acc, x -> acc * x } // 120
4. forEach
遍历元素:
val numbers = listOf(1, 2, 3)
numbers.forEach { println(it) }
5. any/all/none
条件检查:
val numbers = listOf(1, 2, 3, 4, 5)
val hasEven = numbers.any { it % 2 == 0 } // true
val allPositive = numbers.all { it > 0 } // true
val noneNegative = numbers.none { it < 0 } // true
6. find/firstOrNull
查找元素:
val numbers = listOf(1, 2, 3, 4, 5)
val firstEven = numbers.find { it % 2 == 0 } // 2
val firstLarge = numbers.firstOrNull { it > 10 } // null
7. groupBy
分组:
val numbers = listOf(1, 2, 3, 4, 5)
val grouped = numbers.groupBy { it % 2 }
// {1=[1, 3, 5], 0=[2, 4]}
8. 链式调用
多个高阶函数可以链式调用:
val numbers = listOf(1, 2, 3, 4, 5)
val result = numbers
.filter { it > 0 } // [1, 2, 3, 4, 5]
.map { it * 2 } // [2, 4, 6, 8, 10]
.reduce { acc, x -> acc + x } // 30
1.33 高阶函数的性能影响是什么?
答案:
高阶函数的性能考虑:
1. 函数对象开销
高阶函数需要创建函数对象,有额外开销:
// 每次调用都创建Lambda对象
numbers.filter { it > 0 } // 创建函数对象
numbers.map { it * 2 } // 创建函数对象
2. 内联优化
使用inline关键字可以消除函数对象开销:
// 内联函数
inline fun <T> process(list: List<T>, operation: (T) -> T): List<T> {
return list.map(operation)
}
// 编译时展开,无函数对象开销
process(numbers) { it * 2 }
3. 集合操作性能
集合操作可能有性能影响:
// 多次遍历
numbers.filter { it > 0 } // 第一次遍历
.map { it * 2 } // 第二次遍历
.sorted() // 第三次遍历
// 优化:使用序列(延迟计算)
numbers.asSequence()
.filter { it > 0 }
.map { it * 2 }
.sorted()
.toList() // 只遍历一次
4. 最佳实践
- 使用内联函数:减少函数对象开销
- 使用序列:大量数据时提高性能
- 避免过度嵌套:影响可读性和性能
- 合理使用:简单操作可以直接使用,复杂操作考虑性能
1.34 高阶函数与函数式编程的关系
答案:
高阶函数是函数式编程的核心特性:
1. 函数式编程基础
高阶函数是函数式编程的基础,支持:
- 函数作为一等公民
- 函数组合
- 抽象控制结构
2. 不可变数据结构
配合不可变集合使用:
val numbers = listOf(1, 2, 3, 4, 5)
val evens = numbers.filter { it % 2 == 0 } // 返回新列表,不修改原列表
3. 声明式编程
使用高阶函数实现声明式编程:
// 声明式(描述做什么)
val result = numbers
.filter { it > 0 }
.map { it * 2 }
.sum()
// 命令式(描述怎么做)
var sum = 0
for (num in numbers) {
if (num > 0) {
sum += num * 2
}
}
4. 函数组合
高阶函数支持函数组合:
fun <T, R> compose(f: (T) -> R, g: (R) -> R): (T) -> R {
return { x -> g(f(x)) }
}
val double = { x: Int -> x * 2 }
val square = { x: Int -> x * x }
val doubleThenSquare = compose(double, square)
doubleThenSquare(5) // (5 * 2) ^ 2 = 100
5. 实际应用
函数式编程风格:
// 函数式风格
val result = data
.filter { it.isValid() }
.map { it.transform() }
.groupBy { it.category }
.mapValues { it.value.sum() }
1.35 尾递归函数(tailrec)是什么?
答案:
尾递归函数(Tail Recursive Functions)是递归调用发生在函数最后一步的函数,Kotlin使用tailrec关键字优化尾递归。
1. 基本概念
普通递归
fun factorial(n: Int): Int {
return if (n <= 1) 1 else n * factorial(n - 1)
// ❌ 不是尾递归:乘法操作在递归调用之后
}
尾递归
fun factorial(n: Int): Int {
tailrec fun fact(n: Int, acc: Int): Int {
return if (n <= 1) acc else fact(n - 1, acc * n)
// ✅ 尾递归:递归调用是最后一步操作
}
return fact(n, 1)
}
2. tailrec关键字
使用tailrec关键字优化尾递归:
tailrec fun factorial(n: Int, acc: Int = 1): Int {
return if (n <= 1) acc else factorial(n - 1, acc * n)
}
3. 优化原理
编译器将尾递归转换为循环,避免栈溢出:
// 尾递归
tailrec fun factorial(n: Int, acc: Int = 1): Int {
return if (n <= 1) acc else factorial(n - 1, acc * n)
}
// 编译后(伪代码)
fun factorial(n: Int, acc: Int = 1): Int {
var n = n
var acc = acc
while (n > 1) {
acc = acc * n
n = n - 1
}
return acc
}
4. 尾递归条件
- 递归调用是最后一步:递归后不能有其他操作
- 必须是直接递归:不能间接递归
- 不能有try-catch:try-catch会改变控制流
5. 示例
斐波那契数列(尾递归优化)
tailrec fun fibonacci(n: Int, a: Int = 0, b: Int = 1): Int {
return if (n == 0) a else fibonacci(n - 1, b, a + b)
}
列表求和(尾递归优化)
tailrec fun sum(list: List<Int>, acc: Int = 0): Int {
return if (list.isEmpty()) acc else sum(list.drop(1), acc + list.first())
}
6. 与普通递归对比
| 特性 | 普通递归 | 尾递归 |
|---|---|---|
| 栈空间 | O(n) | O(1) |
| 性能 | 较慢 | 较快 |
| 优化 | 编译器不优化 | 编译器优化为循环 |
7. 最佳实践
- 识别尾递归:确保递归调用是最后一步
- 使用tailrec:显式标记尾递归函数
- 使用累加器:通过累加器实现尾递归
- 测试验证:确保函数正确性
8. 注意事项
- 尾递归不是递归的最后一行:而是最后一步操作
- 需要累加器模式:将中间结果作为参数传递
- 编译器检查:如果不是尾递归,编译器会警告
第二章:Kotlin 控制流面试题答案
1. 条件表达式
2.1 Kotlin的if表达式和Java的if语句有什么区别?
答案:
Kotlin的if是表达式(Expression),而Java的if是语句(Statement),这是两者的根本区别。
1. 基本区别
Kotlin(表达式)
// if可以作为表达式,有返回值
val max = if (a > b) a else b
// 可以赋值
val result = if (condition) "Yes" else "No"
Java(语句)
// if是语句,不能直接赋值
int max;
if (a > b) {
max = a;
} else {
max = b;
}
// 需要使用三元运算符
int max = a > b ? a : b;
2. 返回值
Kotlin
// if表达式有返回值
val number = if (x > 0) {
println("Positive")
x // 最后一行是返回值
} else {
println("Negative")
-x
}
Java
// if语句没有返回值,需要显式赋值
int number;
if (x > 0) {
System.out.println("Positive");
number = x;
} else {
System.out.println("Negative");
number = -x;
}
3. 多行表达式
Kotlin
val result = if (condition) {
val temp = calculate()
process(temp)
temp // 返回值
} else {
defaultValue
}
Java
// 需要使用临时变量
int result;
if (condition) {
int temp = calculate();
process(temp);
result = temp;
} else {
result = defaultValue;
}
4. 嵌套使用
Kotlin
val result = if (a > b) {
if (a > c) a else c
} else {
if (b > c) b else c
}
Java
// 需要使用三元运算符嵌套
int result = a > b ? (a > c ? a : c) : (b > c ? b : c);
5. 与when表达式配合
Kotlin
val result = if (x > 0) {
"Positive"
} else if (x < 0) {
"Negative"
} else {
"Zero"
}
// 或者使用when
val result = when {
x > 0 -> "Positive"
x < 0 -> "Negative"
else -> "Zero"
}
6. 优势对比
| 特性 | Kotlin if | Java if |
|---|---|---|
| 类型 | 表达式 | 语句 |
| 返回值 | 有 | 无 |
| 赋值 | 可以直接赋值 | 需要三元运算符 |
| 代码简洁性 | 更简洁 | 相对冗长 |
| 可读性 | 更好 | 一般 |
7. 实际应用
Kotlin
// 初始化变量
val status = if (isActive) "Active" else "Inactive"
// 函数返回值
fun max(a: Int, b: Int) = if (a > b) a else b
// 复杂逻辑
val result = if (condition1) {
value1
} else if (condition2) {
value2
} else {
defaultValue
}
8. 最佳实践
- 优先使用if表达式:代码更简洁
- 避免过度嵌套:复杂逻辑考虑使用when
- 明确返回值:确保所有分支都有返回值
- 类型一致:所有分支返回相同类型
2.2 Kotlin的when表达式是什么?
答案:
when表达式是Kotlin中强大的条件表达式,类似于Java的switch,但功能更强大。
1. 基本语法
when (value) {
value1 -> result1
value2 -> result2
else -> defaultResult
}
2. 基本用法
val x = 2
val result = when (x) {
1 -> "One"
2 -> "Two"
3 -> "Three"
else -> "Other"
}
// result = "Two"
3. 多个值匹配
val result = when (x) {
1, 2, 3 -> "Small"
4, 5, 6 -> "Medium"
else -> "Large"
}
4. 范围匹配
val result = when (x) {
in 1..10 -> "Small"
in 11..100 -> "Medium"
else -> "Large"
}
5. 类型检查
val result = when (obj) {
is String -> obj.length
is Int -> obj * 2
is Boolean -> if (obj) 1 else 0
else -> 0
}
6. 条件表达式
val result = when {
x > 0 && y > 0 -> "Both positive"
x < 0 && y < 0 -> "Both negative"
else -> "Mixed"
}
7. 作为表达式
// when可以作为表达式,有返回值
val status = when (code) {
200 -> "OK"
404 -> "Not Found"
500 -> "Server Error"
else -> "Unknown"
}
8. 多行代码块
val result = when (x) {
1 -> {
val temp = calculate()
process(temp)
temp // 返回值
}
2 -> {
defaultValue
}
else -> {
otherValue
}
}
9. 与Java switch对比
| 特性 | Kotlin when | Java switch |
|---|---|---|
| 类型 | 表达式 | 语句 |
| 返回值 | 有 | 无(Java 14+支持) |
| 类型支持 | 任意类型 | 有限类型(int、String、enum) |
| 范围匹配 | 支持 | 不支持 |
| 类型检查 | 支持 | 不支持 |
| 条件表达式 | 支持 | 不支持 |
| break | 不需要 | 需要(旧版本) |
10. 实际应用
状态处理
val state = when (status) {
"loading" -> showLoading()
"success" -> showContent()
"error" -> showError()
else -> showDefault()
}
类型处理
fun process(value: Any): String {
return when (value) {
is String -> "String: $value"
is Int -> "Int: $value"
is List<*> -> "List with ${value.size} items"
else -> "Unknown type"
}
}
11. 最佳实践
- 优先使用when:比多个if-else更清晰
- 使用else分支:确保覆盖所有情况
- 利用类型检查:使用
is进行类型检查 - 范围匹配:使用
in进行范围匹配
2.3 when表达式和switch语句的区别是什么?
答案:
when表达式和switch语句的主要区别:
1. 类型系统
Kotlin when
// 支持任意类型
when (value) {
"string" -> ...
123 -> ...
true -> ...
MyObject -> ...
}
Java switch
// 只支持有限类型(int、String、enum等)
switch (value) {
case 1: ...
case "string": ... // Java 7+
default: ...
}
2. 返回值
Kotlin when(表达式)
val result = when (x) {
1 -> "One"
2 -> "Two"
else -> "Other"
}
Java switch(语句)
// 旧版本:没有返回值
String result;
switch (x) {
case 1: result = "One"; break;
case 2: result = "Two"; break;
default: result = "Other";
}
// Java 14+:支持表达式
String result = switch (x) {
case 1 -> "One";
case 2 -> "Two";
default -> "Other";
};
3. break语句
Kotlin when
// 不需要break,自动跳出
when (x) {
1 -> println("One")
2 -> println("Two")
}
Java switch
// 需要break,否则会fall through
switch (x) {
case 1:
System.out.println("One");
break; // 必须
case 2:
System.out.println("Two");
break;
}
4. 范围匹配
Kotlin when
when (x) {
in 1..10 -> "Small"
in 11..100 -> "Medium"
else -> "Large"
}
Java switch
// 不支持范围匹配,需要多个case
switch (x) {
case 1:
case 2:
case 3:
// ... 需要列出所有值
break;
}
5. 类型检查
Kotlin when
when (obj) {
is String -> obj.length
is Int -> obj * 2
else -> 0
}
Java switch
// 不支持类型检查,需要使用if-else
if (obj instanceof String) {
// ...
} else if (obj instanceof Integer) {
// ...
}
6. 条件表达式
Kotlin when
when {
x > 0 && y > 0 -> "Both positive"
x < 0 -> "X negative"
else -> "Other"
}
Java switch
// 不支持条件表达式
if (x > 0 && y > 0) {
// ...
} else if (x < 0) {
// ...
}
7. 多值匹配
Kotlin when
when (x) {
1, 2, 3 -> "Small"
4, 5, 6 -> "Medium"
}
Java switch
// Java 14+支持
switch (x) {
case 1, 2, 3 -> "Small";
case 4, 5, 6 -> "Medium";
}
8. 对比总结
| 特性 | Kotlin when | Java switch |
|---|---|---|
| 类型 | 表达式 | 语句(14+支持表达式) |
| 类型支持 | 任意类型 | 有限类型 |
| break | 不需要 | 需要(旧版本) |
| 范围匹配 | 支持 | 不支持 |
| 类型检查 | 支持 | 不支持 |
| 条件表达式 | 支持 | 不支持 |
| 代码简洁性 | 更简洁 | 相对冗长 |
9. 最佳实践
- Kotlin优先使用when:功能更强大
- Java 14+可以使用switch表达式:但功能仍不如when
- 复杂逻辑使用when:比多个if-else更清晰
2.4 when表达式的多种用法有哪些?
答案:
when表达式有多种用法:
1. 值匹配
when (x) {
1 -> "One"
2 -> "Two"
3 -> "Three"
else -> "Other"
}
2. 多值匹配
when (x) {
1, 2, 3 -> "Small"
4, 5, 6 -> "Medium"
7, 8, 9 -> "Large"
else -> "Other"
}
3. 范围匹配
when (x) {
in 1..10 -> "Small"
in 11..100 -> "Medium"
in 101..1000 -> "Large"
else -> "Other"
}
4. 类型检查
when (obj) {
is String -> obj.length
is Int -> obj * 2
is Boolean -> if (obj) 1 else 0
is List<*> -> obj.size
else -> 0
}
5. 条件表达式(无参数)
when {
x > 0 && y > 0 -> "Both positive"
x < 0 && y < 0 -> "Both negative"
x == 0 && y == 0 -> "Both zero"
else -> "Mixed"
}
6. 函数调用
when (x) {
calculateValue() -> "Calculated"
getDefaultValue() -> "Default"
else -> "Other"
}
7. 字符串匹配
when (status) {
"loading" -> showLoading()
"success" -> showContent()
"error" -> showError()
else -> showDefault()
}
8. 枚举匹配
enum class Status { ACTIVE, INACTIVE, PENDING }
when (status) {
Status.ACTIVE -> "Active"
Status.INACTIVE -> "Inactive"
Status.PENDING -> "Pending"
}
9. 多行代码块
val result = when (x) {
1 -> {
val temp = calculate()
process(temp)
temp // 返回值
}
2 -> {
defaultValue
}
else -> {
otherValue
}
}
10. 嵌套when
when (x) {
1 -> when (y) {
1 -> "Both one"
2 -> "X one, Y two"
else -> "X one, Y other"
}
2 -> "X two"
else -> "X other"
}
11. 作为表达式使用
// 赋值
val result = when (x) {
1 -> "One"
2 -> "Two"
else -> "Other"
}
// 函数返回值
fun getStatus(code: Int) = when (code) {
200 -> "OK"
404 -> "Not Found"
500 -> "Server Error"
else -> "Unknown"
}
12. 实际应用示例
状态机
when (state) {
State.INIT -> initialize()
State.LOADING -> loadData()
State.SUCCESS -> showContent()
State.ERROR -> showError()
}
类型处理
fun process(value: Any): String {
return when (value) {
is String -> "String: ${value.length} chars"
is Int -> "Int: $value"
is Double -> "Double: $value"
is List<*> -> "List: ${value.size} items"
is Map<*, *> -> "Map: ${value.size} entries"
else -> "Unknown: ${value::class.simpleName}"
}
}
13. 最佳实践
- 优先使用when:比多个if-else更清晰
- 使用else分支:确保覆盖所有情况
- 利用类型检查:使用
is进行类型检查 - 范围匹配:使用
in进行范围匹配 - 避免过度嵌套:复杂逻辑考虑提取函数
2. 循环
2.5 Kotlin的for循环有哪些用法?
答案:
Kotlin的for循环有多种用法:
1. 遍历范围
// 遍历1到10
for (i in 1..10) {
println(i)
}
// 遍历1到9(不包含10)
for (i in 1 until 10) {
println(i)
}
// 倒序遍历
for (i in 10 downTo 1) {
println(i)
}
// 指定步长
for (i in 1..10 step 2) {
println(i) // 1, 3, 5, 7, 9
}
2. 遍历集合
val list = listOf("a", "b", "c")
// 遍历元素
for (item in list) {
println(item)
}
// 遍历索引和值
for ((index, value) in list.withIndex()) {
println("$index: $value")
}
3. 遍历数组
val array = arrayOf(1, 2, 3, 4, 5)
// 遍历元素
for (item in array) {
println(item)
}
// 遍历索引
for (i in array.indices) {
println("$i: ${array[i]}")
}
// 遍历索引和值
for ((index, value) in array.withIndex()) {
println("$index: $value")
}
4. 遍历Map
val map = mapOf("a" to 1, "b" to 2, "c" to 3)
// 遍历键值对
for ((key, value) in map) {
println("$key: $value")
}
// 只遍历键
for (key in map.keys) {
println(key)
}
// 只遍历值
for (value in map.values) {
println(value)
}
5. 遍历字符串
val str = "Kotlin"
// 遍历字符
for (char in str) {
println(char)
}
// 遍历索引
for (i in str.indices) {
println("$i: ${str[i]}")
}
6. 条件遍历
// 使用if过滤
for (i in 1..10) {
if (i % 2 == 0) {
println(i)
}
}
// 使用filter
for (i in (1..10).filter { it % 2 == 0 }) {
println(i)
}
7. 嵌套循环
for (i in 1..3) {
for (j in 1..3) {
println("$i, $j")
}
}
8. 带标签的循环
outer@ for (i in 1..3) {
inner@ for (j in 1..3) {
if (i == 2 && j == 2) {
break@outer // 跳出外层循环
}
println("$i, $j")
}
}
9. 与Java对比
| 特性 | Kotlin for | Java for |
|---|---|---|
| 语法 | for (item in collection) | for (item : collection) |
| 范围 | 1..10 | for (int i = 1; i <= 10; i++) |
| 倒序 | 10 downTo 1 | for (int i = 10; i >= 1; i--) |
| 步长 | step 2 | i += 2 |
| 索引 | withIndex() | 需要手动处理 |
10. 实际应用
遍历处理
val numbers = listOf(1, 2, 3, 4, 5)
for (num in numbers) {
process(num)
}
索引遍历
val list = listOf("a", "b", "c")
for ((index, value) in list.withIndex()) {
println("Index $index: $value")
}
范围遍历
// 打印乘法表
for (i in 1..9) {
for (j in 1..9) {
print("${i * j}\t")
}
println()
}
11. 最佳实践
- 优先使用for-in:语法简洁
- 使用withIndex():需要索引时使用
- 利用范围:使用
..、until、downTo - 避免修改集合:遍历时不要修改集合
2.6 Kotlin的while和do-while循环
答案:
Kotlin的while和do-while循环:
1. while循环
// 基本语法
while (condition) {
// 循环体
}
// 示例
var i = 0
while (i < 10) {
println(i)
i++
}
2. do-while循环
// 基本语法
do {
// 循环体
} while (condition)
// 示例
var i = 0
do {
println(i)
i++
} while (i < 10)
3. 区别
| 特性 | while | do-while |
|---|---|---|
| 执行次数 | 0次或多次 | 至少1次 |
| 条件检查 | 先检查后执行 | 先执行后检查 |
| 使用场景 | 条件可能不满足 | 至少执行一次 |
4. 实际应用
while循环
// 读取输入直到满足条件
var input: String?
do {
input = readLine()
} while (input != null && input != "quit")
// 处理队列
while (queue.isNotEmpty()) {
val item = queue.poll()
process(item)
}
do-while循环
// 至少执行一次
var choice: String
do {
showMenu()
choice = readLine() ?: ""
processChoice(choice)
} while (choice != "quit")
5. 无限循环
// while无限循环
while (true) {
// 需要break退出
if (condition) break
}
// do-while无限循环
do {
// 需要break退出
if (condition) break
} while (true)
6. 与Java对比
Kotlin的while和do-while与Java基本相同:
// Kotlin
while (condition) { }
do { } while (condition)
// Java
while (condition) { }
do { } while (condition);
7. 最佳实践
- 优先使用for循环:更简洁
- while用于条件循环:条件不明确时使用
- do-while用于至少执行一次:需要先执行后检查时使用
- 避免无限循环:确保有退出条件
2.7 Kotlin的range(区间)是什么?
答案:
Range(区间)是Kotlin中表示连续值范围的类型。
1. 基本语法
// 闭区间(包含两端)
1..10 // 1, 2, 3, ..., 10
// 半开区间(不包含右端)
1 until 10 // 1, 2, 3, ..., 9
// 倒序区间
10 downTo 1 // 10, 9, 8, ..., 1
// 指定步长
1..10 step 2 // 1, 3, 5, 7, 9
2. 类型
Kotlin提供了多种Range类型:
// IntRange
val intRange = 1..10
// CharRange
val charRange = 'a'..'z'
// LongRange
val longRange = 1L..10L
3. 常用操作
遍历
for (i in 1..10) {
println(i)
}
检查包含
val range = 1..10
println(5 in range) // true
println(15 in range) // false
转换为列表
val list = (1..10).toList() // [1, 2, 3, ..., 10]
4. 实际应用
循环
for (i in 1..10) { }
for (i in 1 until 10) { }
for (i in 10 downTo 1) { }
for (i in 1..10 step 2) { }
条件判断
val age = 25
if (age in 18..65) {
println("Working age")
}
when表达式
when (score) {
in 90..100 -> "A"
in 80..89 -> "B"
in 70..79 -> "C"
else -> "F"
}
5. 与Java对比
// Kotlin
for (i in 1..10) { }
// Java
for (int i = 1; i <= 10; i++) { }
6. 最佳实践
- 使用范围:代码更简洁
- 利用in操作符:检查值是否在范围内
- 使用until:需要不包含右端时使用
- 使用step:需要指定步长时使用
2.8 Kotlin的集合遍历方式有哪些?
答案:
Kotlin提供了多种集合遍历方式:
1. for-in循环
val list = listOf("a", "b", "c")
for (item in list) {
println(item)
}
2. forEach
val list = listOf("a", "b", "c")
list.forEach { item ->
println(item)
}
// 使用it
list.forEach { println(it) }
3. forEachIndexed
val list = listOf("a", "b", "c")
list.forEachIndexed { index, value ->
println("$index: $value")
}
4. withIndex()
val list = listOf("a", "b", "c")
for ((index, value) in list.withIndex()) {
println("$index: $value")
}
5. 迭代器
val list = listOf("a", "b", "c")
val iterator = list.iterator()
while (iterator.hasNext()) {
println(iterator.next())
}
6. 索引遍历
val list = listOf("a", "b", "c")
for (i in list.indices) {
println("$i: ${list[i]}")
}
7. Map遍历
val map = mapOf("a" to 1, "b" to 2, "c" to 3)
// 遍历键值对
for ((key, value) in map) {
println("$key: $value")
}
// forEach
map.forEach { (key, value) ->
println("$key: $value")
}
8. 实际应用
列表遍历
val numbers = listOf(1, 2, 3, 4, 5)
// for-in
for (num in numbers) {
process(num)
}
// forEach
numbers.forEach { process(it) }
带索引遍历
val list = listOf("a", "b", "c")
list.forEachIndexed { index, value ->
println("Index $index: $value")
}
9. 最佳实践
- 优先使用for-in:语法简洁
- 需要索引使用forEachIndexed:或withIndex()
- 函数式风格使用forEach:更函数式
- 避免修改集合:遍历时不要修改集合
3. 跳转表达式
2.9 Kotlin的return、break、continue的区别
答案:
return、break、continue的区别:
1. return
return用于从函数中返回:
fun process(x: Int): Int {
if (x < 0) {
return 0 // 从函数返回
}
return x * 2
}
2. break
break用于跳出循环:
for (i in 1..10) {
if (i == 5) {
break // 跳出循环
}
println(i) // 只打印1, 2, 3, 4
}
3. continue
continue用于跳过当前迭代,继续下一次:
for (i in 1..10) {
if (i % 2 == 0) {
continue // 跳过偶数
}
println(i) // 只打印1, 3, 5, 7, 9
}
4. 对比总结
| 关键字 | 作用 | 使用场景 |
|---|---|---|
return | 从函数返回 | 函数中 |
break | 跳出循环 | 循环中 |
continue | 跳过当前迭代 | 循环中 |
5. 实际应用
return
fun findFirstEven(list: List<Int>): Int? {
for (num in list) {
if (num % 2 == 0) {
return num // 找到就返回
}
}
return null
}
break
var sum = 0
for (i in 1..100) {
sum += i
if (sum > 1000) {
break // 超过1000就停止
}
}
continue
for (i in 1..10) {
if (i % 3 == 0) {
continue // 跳过3的倍数
}
process(i)
}
2.10 Kotlin的标签(Label)是什么?
答案:
标签(Label)用于标识代码位置,配合return、break、continue使用。
1. 基本语法
label@ for (i in 1..10) {
// 循环体
}
2. break@label
跳出指定标签的循环:
outer@ for (i in 1..3) {
inner@ for (j in 1..3) {
if (i == 2 && j == 2) {
break@outer // 跳出外层循环
}
println("$i, $j")
}
}
3. continue@label
继续指定标签的循环:
outer@ for (i in 1..3) {
inner@ for (j in 1..3) {
if (j == 2) {
continue@outer // 继续外层循环
}
println("$i, $j")
}
}
4. return@label
从指定标签的Lambda返回:
list.forEach label@ {
if (it < 0) {
return@label // 从Lambda返回,继续下一次迭代
}
process(it)
}
5. 隐式标签
Lambda可以使用隐式标签(函数名):
list.forEach {
if (it < 0) {
return@forEach // 使用隐式标签
}
process(it)
}
6. 实际应用
嵌套循环
outer@ for (i in 1..10) {
for (j in 1..10) {
if (i * j > 50) {
break@outer // 跳出外层循环
}
println("$i * $j = ${i * j}")
}
}
Lambda中的return
fun processList(list: List<Int>) {
list.forEach {
if (it < 0) {
return@forEach // 从Lambda返回
}
println(it)
}
println("Done") // 会执行
}
7. 最佳实践
- 合理使用标签:避免过度使用
- 使用隐式标签:Lambda中优先使用函数名
- 明确标签名:使用有意义的标签名
- 避免深层嵌套:复杂逻辑考虑重构
2.11 Kotlin的return@label用法
答案:
return@label用于从Lambda表达式中返回到指定标签。
1. 基本用法
list.forEach {
if (it < 0) {
return@forEach // 从Lambda返回,继续下一次迭代
}
process(it)
}
2. 显式标签
list.forEach label@ {
if (it < 0) {
return@label // 从标签返回
}
process(it)
}
3. 与普通return的区别
fun processList(list: List<Int>) {
list.forEach {
if (it < 0) {
return // 从函数返回,不会执行后面的代码
}
process(it)
}
println("Done") // 如果return,这行不会执行
}
fun processList2(list: List<Int>) {
list.forEach {
if (it < 0) {
return@forEach // 只从Lambda返回,继续下一次迭代
}
process(it)
}
println("Done") // 会执行
}
4. 实际应用
过滤处理
list.forEach {
if (it < 0) {
return@forEach // 跳过负数
}
process(it)
}
条件处理
data.forEach { item ->
if (!item.isValid()) {
return@forEach // 跳过无效项
}
process(item)
}
5. 最佳实践
- 使用隐式标签:优先使用函数名作为标签
- 明确意图:使用return@label明确表示从Lambda返回
- 避免混淆:不要与普通return混淆
4. 异常处理
2.12 Kotlin的异常处理(try-catch-finally)是什么?
答案:
Kotlin的异常处理使用try-catch-finally结构。
1. 基本语法
try {
// 可能抛出异常的代码
riskyOperation()
} catch (e: Exception) {
// 处理异常
println("Error: ${e.message}")
} finally {
// 无论是否异常都会执行
cleanup()
}
2. 多个catch块
try {
process()
} catch (e: IllegalArgumentException) {
println("Invalid argument: ${e.message}")
} catch (e: IOException) {
println("IO error: ${e.message}")
} catch (e: Exception) {
println("General error: ${e.message}")
}
3. try作为表达式
Kotlin的try可以作为表达式,有返回值:
val result = try {
riskyOperation()
} catch (e: Exception) {
defaultValue
}
// 示例
val number = try {
input.toInt()
} catch (e: NumberFormatException) {
0 // 默认值
}
4. finally块
finally块无论是否异常都会执行:
try {
process()
} catch (e: Exception) {
handleError(e)
} finally {
cleanup() // 总是执行
}
5. 与Java对比
| 特性 | Kotlin | Java |
|---|---|---|
| 语法 | 基本相同 | 基本相同 |
| 表达式 | 可以作为表达式 | 不能作为表达式 |
| 返回值 | 有返回值 | 无返回值 |
| 检查异常 | 不需要声明 | 需要声明 |
6. 实际应用
资源管理
val file = File("data.txt")
try {
file.readText()
} catch (e: IOException) {
println("Cannot read file: ${e.message}")
} finally {
// 清理资源
}
类型转换
val number = try {
input.toInt()
} catch (e: NumberFormatException) {
null
}
7. 最佳实践
- 具体异常类型:捕获具体异常类型
- 使用try表达式:利用返回值特性
- finally清理:确保资源释放
- 避免空catch:至少记录异常
2.13 Kotlin和Java的异常处理有什么区别?
答案:
主要区别:
1. 检查异常
Kotlin
// 不需要声明检查异常
fun readFile() {
File("data.txt").readText() // 可能抛出IOException,但不需要声明
}
Java
// 需要声明检查异常
public void readFile() throws IOException {
Files.readString(Path.of("data.txt"));
}
2. try作为表达式
Kotlin
// try可以作为表达式
val result = try {
riskyOperation()
} catch (e: Exception) {
defaultValue
}
Java
// try不能作为表达式
int result;
try {
result = riskyOperation();
} catch (Exception e) {
result = defaultValue;
}
3. 异常类型
两者都支持:
- 运行时异常(RuntimeException)
- 检查异常(Checked Exception,Java特有)
- 错误(Error)
4. 最佳实践
- Kotlin不需要声明检查异常:更灵活
- 使用try表达式:利用返回值特性
- 具体异常类型:捕获具体异常
2.14 Kotlin的异常类型有哪些?
答案:
Kotlin的异常类型:
1. 异常层次结构
Throwable
├── Error(错误,不应捕获)
│ ├── OutOfMemoryError
│ ├── StackOverflowError
│ └── ...
└── Exception(异常,可以捕获)
├── RuntimeException(运行时异常)
│ ├── IllegalArgumentException
│ ├── NullPointerException
│ ├── IndexOutOfBoundsException
│ └── ...
└── 检查异常(Java特有,Kotlin不需要声明)
├── IOException
├── SQLException
└── ...
2. 常见异常
IllegalArgumentException
if (age < 0) {
throw IllegalArgumentException("Age cannot be negative")
}
NullPointerException
val name: String? = null
val length = name!!.length // 可能抛出NPE
IndexOutOfBoundsException
val list = listOf(1, 2, 3)
val item = list[10] // 可能抛出IndexOutOfBoundsException
3. 自定义异常
class CustomException(message: String) : Exception(message)
// 使用
throw CustomException("Custom error")
4. 最佳实践
- 使用具体异常类型:不要捕获Exception
- 提供有意义的消息:异常消息要清晰
- 避免空catch:至少记录异常
- 自定义异常:需要时创建自定义异常
2.15 如何使用try表达式?
答案:
try表达式可以返回值:
1. 基本用法
val result = try {
riskyOperation()
} catch (e: Exception) {
defaultValue
}
2. 实际应用
类型转换
val number = try {
input.toInt()
} catch (e: NumberFormatException) {
0 // 默认值
}
文件读取
val content = try {
File("data.txt").readText()
} catch (e: IOException) {
"" // 默认值
}
3. 多个catch
val result = try {
process()
} catch (e: IllegalArgumentException) {
"Invalid argument"
} catch (e: IOException) {
"IO error"
} catch (e: Exception) {
"Unknown error"
}
4. 最佳实践
- 利用返回值:使用try表达式简化代码
- 提供默认值:catch块返回合理的默认值
- 具体异常:捕获具体异常类型
5. 类型转换
2.16 Kotlin的类型转换(as、as?)是什么?
答案:
Kotlin的类型转换使用as和as?操作符。
1. 安全转换(as?)
val obj: Any = "Kotlin"
val str: String? = obj as? String // 安全转换,失败返回null
2. 非安全转换(as)
val obj: Any = "Kotlin"
val str: String = obj as String // 非安全转换,失败抛出异常
3. 区别
| 操作符 | 失败时行为 | 返回类型 |
|---|---|---|
as | 抛出异常 | 非空类型 |
as? | 返回null | 可空类型 |
4. 实际应用
安全转换
val obj: Any = "Kotlin"
val str = obj as? String
if (str != null) {
println(str.length)
}
非安全转换
val obj: Any = "Kotlin"
val str = obj as String // 确定类型时使用
println(str.length)
5. 最佳实践
- 优先使用as?:更安全
- 配合空检查:使用as?后检查null
- 确定类型使用as:确定类型时可以使用as
2.17 is和!is关键字的作用是什么?
答案:
is和!is用于类型检查。
1. is(类型检查)
val obj: Any = "Kotlin"
if (obj is String) {
println(obj.length) // 智能转换,obj自动转换为String
}
2. !is(非类型检查)
val obj: Any = "Kotlin"
if (obj !is String) {
println("Not a string")
} else {
println(obj.length) // 智能转换
}
3. 智能转换(Smart Cast)
Kotlin编译器会自动进行类型转换:
val obj: Any = "Kotlin"
if (obj is String) {
// obj自动转换为String,不需要as
println(obj.length)
}
4. when表达式中的使用
when (obj) {
is String -> println(obj.length)
is Int -> println(obj * 2)
is Boolean -> println(if (obj) "True" else "False")
else -> println("Unknown")
}
5. 实际应用
类型处理
fun process(value: Any) {
when (value) {
is String -> println("String: ${value.length}")
is Int -> println("Int: ${value * 2}")
is List<*> -> println("List: ${value.size}")
else -> println("Unknown")
}
}
6. 最佳实践
- 使用is进行类型检查:配合智能转换
- 利用智能转换:不需要显式转换
- when中使用is:处理多种类型
2.18 智能类型转换(Smart Cast)是什么?
答案:
智能类型转换是Kotlin编译器自动进行的类型转换。
1. 基本概念
当编译器确定类型后,自动进行类型转换:
val obj: Any = "Kotlin"
if (obj is String) {
// obj自动转换为String
println(obj.length) // 不需要as转换
}
2. 条件检查
val obj: Any = "Kotlin"
if (obj is String && obj.length > 0) {
// obj自动转换为String
println(obj.uppercase())
}
3. 否定检查
val obj: Any = "Kotlin"
if (obj !is String) {
return
}
// obj自动转换为String
println(obj.length)
4. when表达式
when (obj) {
is String -> obj.length // 自动转换为String
is Int -> obj * 2 // 自动转换为Int
else -> 0
}
5. 限制
智能转换只在编译器可以确定类型时生效:
var obj: Any = "Kotlin"
if (obj is String) {
// obj可能被其他线程修改,不能智能转换
// println(obj.length) // 编译错误
}
// 使用val可以智能转换
val obj2: Any = "Kotlin"
if (obj2 is String) {
println(obj2.length) // 可以智能转换
}
6. 最佳实践
- 使用val:val可以智能转换
- 利用is检查:配合智能转换使用
- 理解限制:var不能智能转换
2.19 类型转换和Java的类型转换有什么区别?
答案:
主要区别:
1. 语法
Kotlin
val str = obj as String // 非安全转换
val str = obj as? String // 安全转换
if (obj is String) { } // 类型检查
Java
String str = (String) obj; // 类型转换
if (obj instanceof String) { } // 类型检查
2. 安全转换
Kotlin
val str = obj as? String // 失败返回null
Java
// 需要手动检查
String str = obj instanceof String ? (String) obj : null;
3. 智能转换
Kotlin
if (obj is String) {
println(obj.length) // 自动转换
}
Java
if (obj instanceof String) {
String str = (String) obj; // 需要显式转换
System.out.println(str.length());
}
4. 最佳实践
- Kotlin使用as?:更安全
- 利用智能转换:减少显式转换
- Java需要显式转换:instanceof后需要转换