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= 引用不可变(相当于 Javafinal),不等于"常量"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 的finalvar(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):
- 编译期检查:编译器会追踪变量的可空性,禁止对可空变量直接调用方法
- 字节码层面:Kotlin 编译器会在 JVM 字节码中插入
null检查指令(Intrinsics.checkNotNullParameter) - 注解标记:编译产物中会添加
@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 的区别?
答案:
when是表达式(有返回值),switch是语句when不需要break,不会穿透(fall-through)when支持任意表达式作为条件(不限于常量)when支持is类型检查、in范围检查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 有缓存)