kotlin面试题-第一部

14 阅读41分钟

第一章:Kotlin 基础概念面试题答案

1. Kotlin 基础

1.1 什么是Kotlin?Kotlin的特点是什么?

答案:

Kotlin是由JetBrains开发的一门静态类型编程语言,运行在Java虚拟机上,也可以编译成JavaScript或Native代码。

Kotlin的主要特点:

  1. 简洁性

    • 语法简洁,代码量比Java减少约20-30%
    • 支持类型推断,减少冗余代码
    • 支持单表达式函数、数据类等语法糖
  2. 安全性

    • 空安全设计,编译时检查空指针异常
    • 不可变集合默认,减少并发问题
    • 类型系统更严格
  3. 互操作性

    • 100%兼容Java,可以调用Java代码
    • 可以逐步迁移,无需重写整个项目
    • 支持Java库和框架
  4. 函数式编程支持

    • 支持Lambda表达式
    • 支持高阶函数
    • 支持不可变数据结构
  5. 工具支持

    • 由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. 数据类

  • Kotlindata 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类型
ObjectAny
voidUnit
intInt
IntegerInt?
StringString
ListList
ArrayListMutableList

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. 互操作最佳实践

  1. 逐步迁移:可以混合使用Java和Kotlin
  2. 空安全处理:Java代码返回的类型需要手动处理空安全
  3. 使用注解:合理使用@JvmStatic@JvmField等注解
  4. 类型映射:了解类型映射规则
  5. 测试验证:确保互操作代码正常工作

10. 实际应用

  • Android开发:Kotlin和Java混合使用
  • Spring Boot:支持Kotlin,可以混合使用
  • Gradle构建:Kotlin DSL和Groovy可以共存

2. 变量和类型

1.6 Kotlin的变量声明方式(var、val)是什么?

答案:

Kotlin使用varval关键字声明变量:

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. 区别对比

特性varval
可变性可变不可变
重新赋值可以不可以
类似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. 最佳实践

  1. 优先使用val:除非需要修改,否则使用val
  2. 明确类型:复杂类型建议显式声明
  3. 避免可变状态:函数式编程风格,减少var使用
  4. 合理使用类型推断:简单类型可以使用类型推断

1.7 var和val的区别是什么?

答案:

varval的主要区别:

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对比

KotlinJava等价
val name = "Kotlin"final String name = "Kotlin";
var name = "Kotlin"String name = "Kotlin";

7. 最佳实践

  1. 默认使用val:除非需要修改
  2. 函数式风格:减少可变状态
  3. 明确意图val表示不可变,var表示可变
  4. 代码审查:检查var的使用是否必要

1.8 Kotlin的基本数据类型有哪些?

答案:

Kotlin的基本数据类型分为两类:数字类型其他类型

1. 数字类型

Kotlin的数字类型都是类,不是原始类型:

类型大小(位)最小值最大值
Byte8-128127
Short16-3276832767
Int32-2³¹2³¹-1
Long64-2⁶³2⁶³-1
Float32IEEE 754
Double64IEEE 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

  • truefalse
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对比

KotlinJava
Intint / Integer
Longlong / Long
Doubledouble / Double
Booleanboolean / Boolean
Charchar / 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. 类型推断的限制

需要显式类型的场景:

  1. 公共API
// 公共函数建议显式类型
public fun process(data: String): Result {
    // ...
}
  1. 复杂类型
// 复杂类型建议显式声明
val complex: Map<String, List<Int>> = mapOf(
    "a" to listOf(1, 2),
    "b" to listOf(3, 4)
)
  1. 可空类型
// 可空类型需要明确
val name: String? = null
  1. 递归函数
// 递归函数需要显式返回类型
fun factorial(n: Int): Int {
    return if (n <= 1) 1 else n * factorial(n - 1)
}

6. 类型推断的优势

  1. 代码简洁:减少冗余代码
  2. 易于重构:修改类型时自动更新
  3. 减少错误:编译器自动检查类型

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. 最佳实践

  1. 简单类型:可以使用类型推断
  2. 复杂类型:建议显式声明
  3. 公共API:建议显式类型
  4. 可读性:平衡简洁性和可读性

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对比

JavaKotlin
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. 最佳实践

  1. 优先使用非空类型:除非确实需要null
  2. 使用安全调用:避免使用!!
  3. 合理使用Elvis操作符:提供默认值
  4. 空检查:利用智能转换

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对比

KotlinJava
"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. 简单变量:使用$变量名
  2. 表达式:使用${表达式}
  3. 复杂表达式:使用${}包裹
  4. 性能考虑:避免在循环中频繁使用复杂表达式

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. 最佳实践

  1. 优先使用字符串模板:简洁、性能好
  2. 大量拼接使用StringBuilder:避免性能问题
  3. 集合拼接使用joinToString:代码更清晰
  4. 避免在循环中使用+:性能差

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对比

KotlinJava等价
str1 == str2str1.equals(str2)
str1 != str2!str1.equals(str2)
str1 === str2str1 == 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. 最佳实践

  1. 优先使用==:简洁、易读
  2. 忽略大小写使用equals():参数清晰
  3. 排序使用compareTo():返回整数便于排序
  4. 空检查使用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对比

KotlinJava等价
fun add(a: Int, b: Int): Intint add(int a, int b)
fun print(): Unitvoid 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对比

KotlinJava
默认参数需要方法重载
命名参数不支持
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. 默认参数的优点

  1. 减少方法重载:一个函数代替多个重载
  2. 代码简洁:避免重复代码
  3. 灵活性:调用时可以选择性指定参数
  4. 向后兼容:添加默认参数不影响现有代码

7. 注意事项

  1. 默认参数在调用处展开:不是编译时展开
  2. 命名参数提高可读性:特别是跳过某些参数时
  3. 合理使用:避免参数过多(建议不超过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. 最佳实践

  1. Boolean参数使用命名参数:提高可读性
  2. 多个参数时使用命名参数:避免混淆
  3. 配合默认参数使用:灵活性更好
  4. 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. 优势

  1. 代码简洁:减少冗余代码
  2. 类型推断:自动推断返回类型
  3. 可读性:简单函数更清晰
  4. 一致性:与Lambda表达式风格一致

6. 注意事项

  1. 仅限单表达式:多个表达式需要普通函数
  2. 复杂逻辑不适用:可读性会降低
  3. 类型推断有限制:复杂类型建议显式声明

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. 最佳实践

  1. 简单函数使用:提高代码简洁性
  2. Getter方法:替代Java的getter
  3. 工具函数:简单转换和计算
  4. 避免过度使用:复杂逻辑使用普通函数

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. 扩展函数的优势

  1. 不修改原类:不需要修改原始类
  2. 代码组织:可以按功能组织代码
  3. API扩展:为第三方库添加功能
  4. 语法简洁:调用方式更自然

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. 合理使用:不要过度使用扩展函数
  2. 命名清晰:使用有意义的函数名
  3. 避免冲突:注意与成员函数的优先级
  4. 文档说明:为扩展函数添加文档注释

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. 最佳实践

  1. 类型推断:充分利用类型推断
  2. it关键字:单个参数时使用it
  3. 尾随Lambda:提高可读性
  4. 避免过长:复杂逻辑使用命名函数

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. 最佳实践

  1. 利用类型推断:减少冗余类型声明
  2. 使用it:单参数时使用it
  3. 合理命名:多参数时使用有意义的参数名
  4. 避免过长:复杂逻辑提取为命名函数

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. 类型推断的优势

  1. 代码简洁:减少冗余类型声明
  2. 易于重构:修改类型时自动更新
  3. 减少错误:编译器自动检查类型

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. 最佳实践

  1. 使用it:单参数时优先使用it
  2. 有意义命名:多参数时使用有意义的参数名
  3. 解构参数:Pair或数据类使用解构
  4. 忽略未使用:使用_忽略未使用的参数

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. 高阶函数的优势

  1. 代码复用:抽象通用模式
  2. 灵活性强:可以动态改变行为
  3. 表达力强:代码更简洁
  4. 函数式编程:支持函数式编程风格

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. 最佳实践

  1. 合理使用:不要过度使用高阶函数
  2. 命名清晰:函数参数使用有意义的名称
  3. 类型明确:复杂类型建议显式声明
  4. 性能考虑:注意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. 使用内联函数:减少函数对象开销
  2. 使用序列:大量数据时提高性能
  3. 避免过度嵌套:影响可读性和性能
  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. 尾递归条件

  1. 递归调用是最后一步:递归后不能有其他操作
  2. 必须是直接递归:不能间接递归
  3. 不能有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. 最佳实践

  1. 识别尾递归:确保递归调用是最后一步
  2. 使用tailrec:显式标记尾递归函数
  3. 使用累加器:通过累加器实现尾递归
  4. 测试验证:确保函数正确性

8. 注意事项

  1. 尾递归不是递归的最后一行:而是最后一步操作
  2. 需要累加器模式:将中间结果作为参数传递
  3. 编译器检查:如果不是尾递归,编译器会警告

第二章: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 ifJava 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. 最佳实践

  1. 优先使用if表达式:代码更简洁
  2. 避免过度嵌套:复杂逻辑考虑使用when
  3. 明确返回值:确保所有分支都有返回值
  4. 类型一致:所有分支返回相同类型

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 whenJava 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. 最佳实践

  1. 优先使用when:比多个if-else更清晰
  2. 使用else分支:确保覆盖所有情况
  3. 利用类型检查:使用is进行类型检查
  4. 范围匹配:使用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 whenJava switch
类型表达式语句(14+支持表达式)
类型支持任意类型有限类型
break不需要需要(旧版本)
范围匹配支持不支持
类型检查支持不支持
条件表达式支持不支持
代码简洁性更简洁相对冗长

9. 最佳实践

  1. Kotlin优先使用when:功能更强大
  2. Java 14+可以使用switch表达式:但功能仍不如when
  3. 复杂逻辑使用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. 最佳实践

  1. 优先使用when:比多个if-else更清晰
  2. 使用else分支:确保覆盖所有情况
  3. 利用类型检查:使用is进行类型检查
  4. 范围匹配:使用in进行范围匹配
  5. 避免过度嵌套:复杂逻辑考虑提取函数

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 forJava for
语法for (item in collection)for (item : collection)
范围1..10for (int i = 1; i <= 10; i++)
倒序10 downTo 1for (int i = 10; i >= 1; i--)
步长step 2i += 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. 最佳实践

  1. 优先使用for-in:语法简洁
  2. 使用withIndex():需要索引时使用
  3. 利用范围:使用..untildownTo
  4. 避免修改集合:遍历时不要修改集合

2.6 Kotlin的while和do-while循环

答案:

Kotlin的whiledo-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. 区别

特性whiledo-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的whiledo-while与Java基本相同:

// Kotlin
while (condition) { }
do { } while (condition)

// Java
while (condition) { }
do { } while (condition);

7. 最佳实践

  1. 优先使用for循环:更简洁
  2. while用于条件循环:条件不明确时使用
  3. do-while用于至少执行一次:需要先执行后检查时使用
  4. 避免无限循环:确保有退出条件

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. 最佳实践

  1. 使用范围:代码更简洁
  2. 利用in操作符:检查值是否在范围内
  3. 使用until:需要不包含右端时使用
  4. 使用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. 最佳实践

  1. 优先使用for-in:语法简洁
  2. 需要索引使用forEachIndexed:或withIndex()
  3. 函数式风格使用forEach:更函数式
  4. 避免修改集合:遍历时不要修改集合

3. 跳转表达式

2.9 Kotlin的return、break、continue的区别

答案:

returnbreakcontinue的区别:

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)用于标识代码位置,配合returnbreakcontinue使用。

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. 最佳实践

  1. 合理使用标签:避免过度使用
  2. 使用隐式标签:Lambda中优先使用函数名
  3. 明确标签名:使用有意义的标签名
  4. 避免深层嵌套:复杂逻辑考虑重构

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. 最佳实践

  1. 使用隐式标签:优先使用函数名作为标签
  2. 明确意图:使用return@label明确表示从Lambda返回
  3. 避免混淆:不要与普通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对比

特性KotlinJava
语法基本相同基本相同
表达式可以作为表达式不能作为表达式
返回值有返回值无返回值
检查异常不需要声明需要声明

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. 最佳实践

  1. 具体异常类型:捕获具体异常类型
  2. 使用try表达式:利用返回值特性
  3. finally清理:确保资源释放
  4. 避免空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. 最佳实践

  1. Kotlin不需要声明检查异常:更灵活
  2. 使用try表达式:利用返回值特性
  3. 具体异常类型:捕获具体异常

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. 最佳实践

  1. 使用具体异常类型:不要捕获Exception
  2. 提供有意义的消息:异常消息要清晰
  3. 避免空catch:至少记录异常
  4. 自定义异常:需要时创建自定义异常

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. 最佳实践

  1. 利用返回值:使用try表达式简化代码
  2. 提供默认值:catch块返回合理的默认值
  3. 具体异常:捕获具体异常类型

5. 类型转换

2.16 Kotlin的类型转换(as、as?)是什么?

答案:

Kotlin的类型转换使用asas?操作符。

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. 最佳实践

  1. 优先使用as?:更安全
  2. 配合空检查:使用as?后检查null
  3. 确定类型使用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. 最佳实践

  1. 使用is进行类型检查:配合智能转换
  2. 利用智能转换:不需要显式转换
  3. 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. 最佳实践

  1. 使用val:val可以智能转换
  2. 利用is检查:配合智能转换使用
  3. 理解限制: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. 最佳实践

  1. Kotlin使用as?:更安全
  2. 利用智能转换:减少显式转换
  3. Java需要显式转换:instanceof后需要转换