2、Kotlin语言基础:变量、常量、基本数据类型、类型推断、字符串模板、条件表达式、循环、when表达式

Kotlin 作为 Jetpack Compose 官方推荐的开发语言,其简洁、安全、表达力强的特性在 UI 声明式编程中体现得淋漓尽致。无论是定义状态、处理用户交互,还是构建复杂的业务逻辑,变量、控制流等基础语法都是每一行 Compose 代码的基石。本章将深入剖析 Kotlin 语言的核心基础,帮助你建立扎实的语法功底,为后续学习 Composable 函数、状态管理以及高级 UI 组件打下坚实基础。我们将从变量与常量的声明规则出发,逐步掌握类型推断、字符串模板、条件与循环控制,并通过实战案例理解这些语法在 Compose 开发中的真实应用场景。

Kotlin 基础语法知识框架 变量与常量 • var: 可变变量 • val: 只读常量 • 类型推断: 编译器自动推导 • 显式类型声明 • 顶层变量/局部变量 基本数据类型 • 数值: Byte/Short/Int/Long • 浮点: Float/Double • 布尔: Boolean • 字符: Char • 字符串: String 控制流 • if / else 条件表达式 • when 表达式(强大分支) • for 循环(迭代) • while / do-while 循环 • 循环控制: break/continue 字符串模板 • \$variable 直接取值 • \${expression} 表达式求值 类型推断 • val name = "Kotlin" • 编译期静态类型检查 Compose 应用场景 • 状态声明: var by remember • 条件渲染: if/when Kotlin 语法设计哲学 简洁安全 · 表达力强 · 空安全 · 函数式与面向对象融合 所有语法特性最终服务于构建声明式、响应式的 Compose UI

2.1 变量与常量:val 与 var 的正确选择

核心原理

Kotlin 中声明变量使用 var(可变)和 val(不可变)两个关键字。val 类似于 Java 中的 final,一旦赋值便不可修改;var 则允许重新赋值。在 Jetpack Compose 中,强烈推荐优先使用 val,因为不可变性能够显著降低 UI 状态管理的复杂度,避免因变量意外修改导致的界面不一致问题。Kotlin 编译器会强制检查 val 的不可变性,任何尝试重新赋值的操作都会导致编译错误。

核心原则:默认使用 val,仅在确有必要时才使用 var。这不仅是编码规范,更是函数式编程思想的体现——不可变数据天然线程安全,且更容易追踪状态变化。

类型推断与显式声明

Kotlin 具备强大的类型推断能力,编译器能够根据初始值自动推导变量类型。例如 val count = 10 会自动推断为 Int 类型。但某些场景下需要显式声明类型,比如变量初始值为 null 或希望使用父类型时。显式声明的语法为 val name: String = "Kotlin"。在 Compose 中,当声明状态变量时,类型推断与显式声明经常结合使用:

// 类型推断
val isVisible = true  // Boolean
val title = "Hello"   // String
val price = 19.99     // Double

// 显式类型声明
val count: Int = 100
val message: String? = null  // 可空类型必须显式声明

实战场景:Compose 中的状态声明

在 Compose 中,我们经常使用 var 配合 remember 来声明可变状态,但对外暴露时通常使用 val 只读接口。这种模式既保证了内部可变性,又对外提供了不可变视图:

@Composable
fun Counter() {
    // 内部使用 var 管理可变状态
    var count by remember { mutableStateOf(0) }
    
    // 对外展示使用 val 读取
    val currentCount = count
    
    Button(onClick = { count++ }) {
        Text("点击次数: $currentCount")
    }
}

常见误区与总结

  • 误区一:认为 val 等同于 Java 的 final。实际上 val 只保证引用不可变,如果指向的是可变对象(如 MutableList),对象内部仍可修改。
  • 误区二:过度使用 var。在 Compose 中,过多的可变状态会导致重组频繁,影响性能。
  • 误区三:忽略类型推断的边界。当表达式复杂或涉及泛型时,显式声明类型能提高代码可读性。

2.2 基本数据类型与类型系统

核心原理

Kotlin 的基本数据类型分为数值型(Byte、Short、Int、Long、Float、Double)、布尔型(Boolean)、字符型(Char)和字符串型(String)。与 Java 不同,Kotlin 中所有类型都是对象,没有原始类型与包装类型的区分。这意味着你可以直接调用类型的方法,例如 10.toString()。Kotlin 的类型系统还引入了空安全设计,通过 ? 后缀区分可空类型与非空类型,从编译层面杜绝了空指针异常。

类型位数取值范围Compose 典型应用
Byte8-128 ~ 127二进制数据处理
Short16-32768 ~ 32767节省内存的数值存储
Int32-2^31 ~ 2^31-1计数、索引、尺寸
Long64-2^63 ~ 2^63-1时间戳、大数值计算
Float32约 ±3.4E38动画插值、透明度
Double64约 ±1.7E308精确计算、坐标
Boolean-true / false条件判断、开关状态
Char16Unicode 字符单字符输入
String-不可变字符序列文本显示、用户输入

类型转换与比较

Kotlin 不支持隐式类型转换,必须显式调用转换函数。例如 Int 转 Long 需要使用 toLong()。比较运算中,== 比较结构相等性(内容),=== 比较引用相等性。在 Compose 中,状态比较通常使用 == 判断内容是否变化,以决定是否触发重组。

val intValue: Int = 42
val longValue: Long = intValue.toLong()  // 显式转换
val doubleValue: Double = 3.14
val intFromDouble = doubleValue.toInt()  // 截断小数部分

// 字符串与数值互转
val numStr = "123"
val num = numStr.toIntOrNull() ?: 0  // 安全转换,失败返回 null

实战场景:Compose 中的数值处理

在 UI 开发中,经常需要处理尺寸、颜色、动画等数值。Kotlin 的类型安全特性能够避免许多潜在错误:

@Composable
fun ProgressBar(progress: Float) {
    // Float 类型确保进度值在 0f-1f 之间
    val percentage = (progress * 100).toInt()
    Text("进度: $percentage%")
    
    // 使用 Long 类型处理时间戳
    val timestamp = System.currentTimeMillis()
    val formattedTime = timestamp.toString()
}

常见误区与总结

  • 误区一:混淆 == 与 ===。在 Compose 中,状态比较应使用 == 判断内容变化。
  • 误区二:忽略空安全。可空类型必须进行空处理,否则编译不通过。
  • 误区三:过度使用 Float 导致精度丢失。涉及金额等精确计算时应使用 Double 或 BigDecimal。

2.3 字符串模板与表达式

核心原理

字符串模板是 Kotlin 的一大亮点,允许在字符串中直接嵌入变量和表达式,使用 $ 符号引用变量,使用 ${} 包裹复杂表达式。这一特性极大地简化了字符串拼接操作,使代码更加简洁易读。在 Compose 中,字符串模板常用于动态文本展示、格式化输出等场景。

val name = "Kotlin"
val version = 2.0
val message = "欢迎使用 $name 版本 ${version}"

// 复杂表达式
val price = 99.99
val discount = 0.8
val finalPrice = "折后价: ${"$"}${String.format("%.2f", price * discount)}"

// 转义字符
val path = "C:\\Users\\Admin"  // 使用双反斜杠
val rawString = """多行文本
    使用原始字符串
    保留格式"""

实战场景:Compose 中的动态文本

在 UI 中,字符串模板让文本内容与状态变量紧密结合,实现响应式更新:

@Composable
fun UserGreeting(userName: String, loginCount: Int) {
    Text(
        text = "欢迎回来,$userName!\n这是您第 ${loginCount + 1} 次登录",
        style = MaterialTheme.typography.bodyLarge
    )
    
    // 条件表达式嵌入
    val status = if (loginCount > 10) "VIP用户" else "普通用户"
    Text("会员等级: $status")
}

常见误区与总结

  • 误区一:忘记转义 $ 符号。在字符串模板中,$ 是特殊字符,如需显示字面量应使用 \$。
  • 误区二:过度使用模板导致可读性下降。当表达式过于复杂时,应提取为独立变量。
  • 误区三:忽略多行字符串的缩进处理。原始字符串会保留所有空格和换行。

2.4 条件表达式与 when 分支

核心原理

Kotlin 的 if 是表达式而非语句,可以返回值。when 则替代了 Java 的 switch,但功能更强大:支持任意类型、范围判断、条件组合,同样可以作为表达式使用。在 Compose 中,条件表达式和 when 分支是构建动态 UI 的核心工具,用于根据状态决定渲染内容。

// if 作为表达式
val max = if (a > b) a else b

// when 表达式
fun describe(obj: Any): String = when (obj) {
    1 -> "数字一"
    "Hello" -> "问候语"
    is Long -> "长整型"
    !is String -> "不是字符串"
    else -> "未知类型"
}

// when 范围判断
val score = 85
val grade = when (score) {
    in 90..100 -> "优秀"
    in 80..89 -> "良好"
    in 60..79 -> "及格"
    else -> "不及格"
}

实战场景:Compose 中的条件渲染

在 Compose 中,条件表达式控制 UI 的显示与隐藏,when 分支处理多状态切换:

@Composable
fun LoadStateView(state: LoadState) {
    when (state) {
        is LoadState.Loading -> CircularProgressIndicator()
        is LoadState.Success -> ContentList(state.data)
        is LoadState.Error -> ErrorMessage(state.message)
    }
}

@Composable
fun PriceDisplay(price: Double, isMember: Boolean) {
    val displayPrice = if (isMember) price * 0.9 else price
    Text(
        text = if (displayPrice > 100) "高价商品: $displayPrice" else "优惠价格: $displayPrice",
        color = if (displayPrice > 100) Color.Red else Color.Green
    )
}

常见误区与总结

  • 误区一:忘记 when 的穷尽性检查。当 when 作为表达式时,必须覆盖所有可能分支或提供 else。
  • 误区二:在 if 表达式中使用多条语句。if 作为表达式时,最后一个表达式作为返回值。
  • 误区三:过度嵌套条件。应使用 when 或提前返回简化逻辑。

2.5 循环控制:for、while 与迭代器

核心原理

Kotlin 提供 for、while、do-while 三种循环结构。for 循环主要用于遍历集合、数组或范围,语法简洁;while 适用于条件控制的循环。在 Compose 中,循环常用于生成列表、处理集合数据,但需要注意避免在 Composable 函数中执行耗时循环操作。

// for 循环遍历范围
for (i in 1..5) { println(i) }  // 包含 5
for (i in 1 until 5) { println(i) }  // 不包含 5
for (i in 10 downTo 1 step 2) { println(i) }  // 递减步长

// 遍历集合
val fruits = listOf("苹果", "香蕉", "橙子")
for (fruit in fruits) { println(fruit) }
for ((index, fruit) in fruits.withIndex()) { println("$index: $fruit") }

// while 循环
var count = 0
while (count < 5) {
    println("计数: $count")
    count++
}

实战场景:Compose 中的列表渲染

在 Compose 中,循环通常与 LazyColumn 或 forEach 结合使用,但需要注意性能优化:

@Composable
fun NumberList(numbers: List) {
    // 使用 forEach 处理集合
    Column {
        numbers.forEach { number ->
            Text("数字: $number")
        }
    }
    
    // 使用 LazyColumn 高效渲染大量数据
    LazyColumn {
        items(numbers) { number ->
            ListItem(number)
        }
    }
}

// 循环中的条件控制
fun processNumbers(numbers: List): List {
    val result = mutableListOf()
    for (number in numbers) {
        if (number % 2 == 0) continue  // 跳过偶数
        if (number > 100) break        // 超过 100 终止
        result.add(number)
    }
    return result
}

常见误区与总结

  • 误区一:在 Compose 重组中使用大循环。应使用 remember 缓存计算结果。
  • 误区二:混淆 until 与 .. 的范围边界。
  • 误区三:在循环中修改正在遍历的集合,可能导致并发修改异常。
经验总结:Kotlin 的基础语法设计处处体现"简洁、安全、表达力"三大原则。在 Compose 开发中,合理运用 val/var 选择、类型推断、字符串模板、when 表达式和循环控制,能够显著提升代码质量和开发效率。建议在编写每个 Composable 函数时,先思考状态的可变性设计,再选择合适的控制流结构,最后用字符串模板优雅地展示动态内容。
风险提示:虽然 Kotlin 语法简洁,但过度使用高级特性(如复杂的 when 分支、嵌套循环)会降低代码可读性。在团队协作中,应保持代码风格统一,优先选择最直观的语法实现功能。同时,注意 Compose 的重组机制,避免在 UI 渲染路径中执行不必要的循环和计算。