跳到主要内容

Kotlin 基础语法

问题

Kotlin 的基础语法有哪些核心特性?与 Java 相比有哪些改进?

答案

1. 变量声明

// val —— 只读变量(类似 Java 的 final)
val name: String = "Android"
val age = 25 // 类型推断

// var —— 可变变量
var count = 0
count++ // ✅ 可以重新赋值

// val 不等于不可变!引用不可变,但对象内容可变
val list = mutableListOf(1, 2, 3)
list.add(4) // ✅ 引用不变,内容可变
val vs var 面试要点
  • val = 引用不可变(相当于 Java final),不等于"常量"
  • var = 引用可变
  • 编译时常量使用 const val(仅限 top-level 或 companion object 中的基本类型/String)

2. 空安全

Kotlin 将空安全融入类型系统,编译期就能发现空指针问题:

// 非空类型 —— 不能赋值 null
var name: String = "Kotlin"
name = null // ❌ 编译错误

// 可空类型 —— 类型后加 ?
var nickname: String? = "K"
nickname = null // ✅

// 安全调用 ?.
val length = nickname?.length // nickname 为 null 时返回 null

// Elvis 操作符 ?:
val len = nickname?.length ?: 0 // nickname 为 null 时返回 0

// 非空断言 !! (尽量避免使用)
val len2 = nickname!!.length // nickname 为 null 时抛出 NPE

// 安全类型转换
val num: Int? = str as? Int // 转换失败返回 null 而非抛异常

// let 配合安全调用
nickname?.let {
// 这里 it 一定非空
println("Nickname is $it, length = ${it.length}")
}

3. 控制流

// when 表达式 —— 取代 switch,功能更强大
fun describe(obj: Any): String = when (obj) {
1 -> "One"
"Hello" -> "Greeting"
is Long -> "Long: $obj"
!is String -> "Not a string"
else -> "Unknown"
}

// when 也可以无参数,替代 if-else 链
fun classify(score: Int) = when {
score >= 90 -> "A"
score >= 80 -> "B"
score >= 60 -> "C"
else -> "F"
}

// if 是表达式,可以有返回值
val max = if (a > b) a else b

// for 循环
for (i in 1..10) { } // 1 到 10(包含)
for (i in 1 until 10) { } // 1 到 9(不包含 10)
for (i in 10 downTo 1) { } // 10 到 1
for (i in 1..10 step 2) { } // 1, 3, 5, 7, 9

// 解构声明
for ((index, value) in list.withIndex()) {
println("$index: $value")
}

4. 函数

// 标准函数声明
fun sum(a: Int, b: Int): Int {
return a + b
}

// 单表达式函数
fun sum(a: Int, b: Int): Int = a + b
fun sum(a: Int, b: Int) = a + b // 返回类型推断

// 默认参数
fun greet(name: String, greeting: String = "Hello") = "$greeting, $name!"
greet("World") // "Hello, World!"
greet("World", "Hi") // "Hi, World!"

// 命名参数
greet(greeting = "Hey", name = "Kotlin")

// 可变参数
fun printAll(vararg messages: String) {
messages.forEach { println(it) }
}
printAll("Hello", "World") // 传入多个参数
printAll(*arrayOf("A", "B")) // 展开数组

5. 字符串模板

val name = "Kotlin"
println("Hello, $name!") // 变量引用
println("Length: ${name.length}") // 表达式
println("Raw string: ${'$'}9.99") // 转义 $ 符号

// 多行字符串
val json = """
{
"name": "$name",
"version": 2.0
}
""".trimIndent()

6. 类型检查与转换

// is 操作符 + 智能转换(Smart Cast)
fun process(obj: Any) {
if (obj is String) {
// 这里 obj 自动转换为 String 类型,无需显式转换
println(obj.length)
}

// when 中同样支持智能转换
when (obj) {
is Int -> println(obj + 1)
is String -> println(obj.uppercase())
is List<*> -> println(obj.size)
}
}

// 显式类型转换
val num: Int = str as Int // 不安全转换,失败抛 ClassCastException
val num: Int? = str as? Int // 安全转换,失败返回 null

常见面试问题

Q1: val 和 var 的区别?val 就是不可变的吗?

答案

  • val(value):引用不可变,一旦初始化后不能重新赋值,类似 Java 的 final
  • var(variable):引用可变,可以重新赋值

val 不等于不可变val 只保证引用不变,但对象本身的内容可以变:

val list = mutableListOf(1, 2, 3)
list.add(4) // ✅ 引用没变,但列表内容变了

// 真正的不可变需要使用不可变集合
val immutableList = listOf(1, 2, 3)
// immutableList.add(4) // ❌ 没有 add 方法

Q2: Kotlin 的空安全是如何实现的?

答案

Kotlin 在 类型系统层面 区分了可空类型(String?)和非空类型(String):

  1. 编译期检查:编译器会追踪变量的可空性,禁止对可空变量直接调用方法
  2. 字节码层面:Kotlin 编译器会在 JVM 字节码中插入 null 检查指令(Intrinsics.checkNotNullParameter
  3. 注解标记:编译产物中会添加 @Nullable@NotNull 注解,方便 Java 互操作
// 编译后等价的伪代码
fun greet(name: String) {
Intrinsics.checkNotNullParameter(name, "name") // 运行时检查
println("Hello, $name")
}

Q3: ?.?:!!as? 分别是什么?

答案

操作符名称说明
?.安全调用对象为 null 时短路返回 null
?:Elvis 操作符左侧为 null 时返回右侧默认值
!!非空断言强制断言非空,为 null 时抛 NPE
as?安全转换转换失败返回 null 而非抛异常

最佳实践:优先使用 ?.?:,尽量避免 !!

Q4: Kotlin 中 when 和 Java switch 的区别?

答案

  1. when表达式(有返回值),switch 是语句
  2. when 不需要 break,不会穿透(fall-through)
  3. when 支持任意表达式作为条件(不限于常量)
  4. when 支持 is 类型检查、in 范围检查
  5. when 可以无参数使用,替代 if-else

Q5: Kotlin 中 ===== 的区别?

答案

  • ==结构相等,等价于 Java 的 equals(),会先检查 null
  • ===引用相等,等价于 Java 的 ==,检查是否是同一个对象
val a = "Hello"
val b = "Hello"
println(a == b) // true(内容相等)
println(a === b) // true(字符串池优化,指向同一对象)

val c = Integer(127)
val d = Integer(127)
println(c == d) // true(值相等)
println(c === d) // 可能为 true(-128~127 有缓存)

相关链接