Kotlin 系列开篇:从 Java 转 Kotlin,这些基础语法你得先搞明白

10 阅读4分钟

Kotlin 系列开篇:从 Java 转 Kotlin,这些基础语法你得先搞明白

说真的,我见过太多人学 Kotlin 从「变量声明」这一步就开始踩坑。今天这篇文章,就是帮你把最基础、但最容易混淆的概念一次性讲清楚。

1. 变量声明:var 和 val 的选择

Kotlin 的变量声明就两个关键字,但坑最多:

// val:只读,不可修改,等价 Java 的 final
val name = "张三"
// name = "李四"  // ❌ 编译报错

// var:可变,可以多次赋值
var age = 25
age = 26  // ✅ 没问题

实战建议:默认用 val,只有需要修改的地方才用 var。这样做有两个好处:

  1. 代码意图更清晰,一眼看出哪些数据会变
  2. 编译器帮你做不可变性检查,减少 bug
// 业务场景中的典型用法
data class User(val name: String, var age: Int)
// name 不可变(用户姓名一般不会改)
// age 可变(年龄会变)

2. 空安全:Kotlin 的杀手锏

这是 Kotlin 和 Java 最大的区别,也是我最推荐的功能。

// 普通类型:不能赋值 null
var name: String = "张三"
// name = null  // ❌ 编译报错

// 可空类型:类型后面加 ?,才能赋值 null
var nickname: String? = null  // ✅ 没问题
nickname = "小张"
nickname = null

安全调用三件套

var name: String? = "张三"

// 1. 安全调用 ?. :为 null 直接返回 null,不报错
val len = name?.length  // len 是 Int? 类型

// 2. 空合并 ?: :为 null 时使用默认值
val len = name?.length ?: 0  // 为 null 返回 0

// 3. 强行调用 !!. :相信我这里一定不为 null
val len = name!!.length  // 为 null 直接崩溃,不推荐

实战中的典型用法

// Android 开发中的典型场景
val bundle = intent.extras
val userId = bundle?.getString("user_id") ?: "0"
val age = bundle?.getInt("age") ?: 0

3. 类型推断:编译器比你更聪明

Kotlin 编译器会根据右边的值自动推断类型,你不需要手动声明:

// 编译器自动推断为 Int
val num = 100

// 编译器自动推断为 String
val name = "张三"

// 显式声明类型(有时候是必要的)
val num: Int = 100
val list: List<String> = arrayListOf()

什么时候必须显式声明类型

// 1. 变量声明时没有初始化
lateinit var binding: ActivityMainBinding

// 2. 泛型相关
val map: Map<String, Int> = mapOf()

// 3. 函数返回类型(复杂类型建议声明)
fun parseJson(json: String): User? { ... }

4. 字符串模板:告别拼接地狱

val name = "张三"
val age = 25

// 简单变量嵌入
val info = "姓名:$name,年龄:$age"
println(info)  // 姓名:张三,年龄:25

// 表达式嵌入
val info2 = "明年年龄:${age + 1}"
val info3 = "名字长度:${name.length}"

多行字符串(做日志、JSON 格式化时特别有用):

val json = """
    {
        "name": "$name",
        "age": $age
    }
""".trimIndent()

5. 条件控制:if 和 when

if 表达式(Kotlin 的 if 是有返回值的)

// Java 写法
int max;
if (a > b) {
    max = a;
} else {
    max = b;
}

// Kotlin 简化写法
val max = if (a > b) a else b

when 表达式(Kotlin 的超级 switch)

val grade = 85
val result = when {
    grade >= 90 -> "A"
    grade >= 80 -> "B"
    grade >= 60 -> "C"
    else -> "D"
}

when 的强大用法

// 带条件判断
when (x) {
    in 1..10 -> print("1到10之间")
    !in 20..30 -> print("不在20到30之间")
    is String -> print("是字符串")
}

// 替代 if-else 链
when {
    x > 0 -> print("正数")
    x < 0 -> print("负数")
    else -> print("零")
}

6. 循环:for 和 Range

Range(Kotlin 特有的区间表达)

// 闭区间:包含起始和结束
for (i in 1..5) print(i)  // 1 2 3 4 5

// 左闭右开:不包含结束
for (i in 1 until 5) print(i)  // 1 2 3 4

// 倒序
for (i in 5 downTo 1) print(i)  // 5 4 3 2 1

// 步长
for (i in 1..10 step 2) print(i)  // 1 3 5 7 9

遍历集合

val list = listOf("A", "B", "C")

// 普通遍历
for (item in list) {
    println(item)
}

// 带索引遍历
for ((index, item) in list.withIndex()) {
    println("$index: $item")
}

// 遍历 Map
val map = mapOf("A" to 1, "B" to 2)
for ((key, value) in map) {
    println("$key -> $value")
}

7. 空安全与智能转换

is 关键字(等价 Java 的 instanceof)

fun printLength(obj: Any) {
    // 使用 is 判断类型
    if (obj is String) {
        // Kotlin 自动转换,无需强制类型转换
        println("字符串长度:${obj.length}")
    } else if (obj is Int) {
        println("整数值:${obj}")
    }
}

智能转换的原理:Kotlin 编译器会跟踪 is 检查,在确认类型的作用域内自动转换。

8. 解构声明:一次性拆出多个变量

解构声明是 Kotlin 的语法糖,可以把一个对象的多个属性一次性拆解给多个变量:

data class User(val name: String, val age: Int, val city: String)

val user = User("张三", 25, "北京")

// 解构:一次性拆出三个变量
val (name, age, city) = user
println("$name, $age, $city")  // 张三, 25, 北京

// 忽略不需要的属性
val (name, _, city) = user  // age 被忽略

// Map 遍历解构(最常用)
val map = mapOf("name" to "张三", "age" to "25")
map.forEach { (key, value) ->
    println("$key: $value")
}

底层原理:编译器会把解构转换为对 componentN() 函数的调用,data class 会自动生成这些函数。

9. 编译期 vs 运行期空安全

Kotlin 的空安全分为两个阶段:

编译期空安全(主要)

var name: String = "张三"
// name = null  // ❌ 编译直接报错,从源头杜绝空指针

var nickname: String? = null  // 可空类型
// nickname.length  // ❌ 编译报错,不能直接调用
nickname?.length  // ✅ 安全调用

运行期空安全(兜底)

有些场景编译器管不了,只能运行期处理:

// 1. 非空断言 !!:编译通过,运行才爆
var name: String? = null
name!!.length  // 编译能过,运行抛异常

// 2. lateinit:编译不检查,运行才爆
lateinit var binding: ActivityMainBinding
binding.tvTitle.text = "标题"  // 如果没初始化,运行抛异常

// 3. Java 互操作:平台类型 T!
val text = javaMethod()  // 返回类型是 String!,编译器不校验
text.length  // 运行可能崩

记住:编译期空安全是主力,运行期空安全是兜底。能用编译期检查解决的,绝不要留给运行期。

10. 注释:支持嵌套

// 单行注释

/*
 * 多行注释
 */

/*
 * Kotlin 支持嵌套注释,Java 不支持
 * /* 这里还可以写注释 */
 */

总结

Kotlin 基础语法就这些核心点:

表格

特性核心用法
val vs var默认 val,可变才用 var
空安全? 声明、?. 调用、?: 默认值
类型推断编译器自动推断,复杂类型显式声明
字符串模板$变量 嵌入、${表达式} 复杂场景
when超级 switch,支持条件和区间
Range..untildownTostep