前置知识: Java

Kotlin 基础语法

14 min入门

Kotlin 基础语法完整讲解:变量声明、基本类型、字符串模板、包与导入、控制流与区间。

前置知识

学习目标

  • 掌握「1. 历史动机与发展脉络」的核心机制、典型用法与常见陷阱
  • 掌握「2. 形式化定义」的核心机制、典型用法与常见陷阱
  • 掌握「3. 理论推导与原理解析」的核心机制、典型用法与常见陷阱
  • 掌握「4. 代码示例(带详尽注释)」的核心机制、典型用法与常见陷阱
  • 掌握「5. 对比分析」的核心机制、典型用法与常见陷阱

1. 历史动机与发展脉络

Kotlin 由 JetBrains 于 2010 年开始研发,2011 年公开,2016 年 2 月发布 1.0。设计动机是解决 Java 的长期痛点:冗长(样板代码)、空指针风险、类型推断不足、函数式支持薄弱。Kotlin 与 Java 100% 互操作,编译器(kotlinc)输出 JVM 字节码,因此可以在既有 Java 项目中渐进采用。

2017 年 Google 宣布 Kotlin 成为 Android 一级开发语言;2019 年 Android 官方推荐 Kotlin-first;2023 年 Kotlin 2.0 发布,引入 K2 编译器(基于 FIR 前端),编译速度与内存占用显著改善。Kotlin Multiplatform(KMP)支持 JVM、Android、iOS、WebAssembly 与原生目标,JetBrains 与 Google 在 Compose Multiplatform 上持续推进跨平台 UI 方案。

Kotlin 版本节奏:1.x 时代每半年左右发布小版本;2.0 起保持每年大版本演进(2.1、2.2 等),K2 编译器默认启用,kotlinx 生态(coroutines、serialization、datetime)同步发展。

timeline
    title Kotlin 演进
    2010 : JetBrains 启动 Kotlin 项目
    2011 : 首次公开亮相
    2016 : Kotlin 1.0 发布
    2017 : Android 一级语言支持
    2019 : Android Kotlin-first 官方定位
    2023 : Kotlin 2.0,K2 编译器
    2025 : KMP 与 Compose Multiplatform 稳定演进

2. 形式化定义

2.1 变量声明

val 名称: 类型 = 值:只读引用,初始化后不可重新赋值(但引用的对象内部状态可变);

var 名称: 类型 = 值:可变引用,可重新赋值;

类型推断:val count = 42 推断为 Int;val name = "Kotlin" 推断为 String。

顶层声明:Kotlin 允许在文件顶层声明变量与函数,无需类包装。

2.2 基本类型

数值:Byte、Short、Int、Long(后缀 L)、Float(后缀 F)、Double;

布尔:Boolean(true/false);

字符:Char(单引号);

字符串:String(双引号),不可变;

无符号类型(实验性到稳定):UInt、ULong 等;

Kotlin 类型都是对象,但数值类型在 JVM 上尽量装箱/拆箱优化(Int 映射 int 或 Integer)。

2.3 字符串模板

"$variable" 直接插入变量;"${expression}" 插入表达式;$ 本身用 \$ 转义。模板在编译期展开为字符串拼接或 StringBuilder,支持任意表达式(包括函数调用与属性访问)。

2.4 控制流

if:可作表达式,返回分支值;

when:替代 Java 的 switch,支持任意条件(常量、类型检查、区间、表达式、无参数分支),也可作表达式;

for:迭代任何提供迭代器的对象,常用 for (x in 0..10);

while/do-while:与 Java 语义一致;

break/continue 与标签(label)配合支持跳出嵌套循环。

2.5 区间

a..b:闭区间(包含 b);

a until b:半开区间(不含 b);

a downTo b:递减区间;

step n:步长;

区间支持 in 运算符检查成员关系。

2.6 类型检查与转换

is:类型检查,智能转换(smart cast)在不可变上下文中自动生效;

as:强制转换;as?:安全转换,失败返回 null;

可空类型:Type?;安全调用 ?.;Elvis ?:;非空断言 !!。

flowchart LR
    A["Kotlin 源码 .kt"] --> B["kotlinc / K2 编译器"]
    B --> C["JVM 字节码 .class"]
    B --> D["JavaScript / Wasm"]
    B --> E["原生二进制"]
    C --> F["与 Java 互操作"]

3. 理论推导与原理解析

3.1 空安全类型系统

Kotlin 把可空性编码进类型系统:String 与 String? 是不同静态类型。编译器在调用链上强制处理空值:?. 短路返回 null,?: 提供默认值,!! 显式声明“我确定非空”(失败抛 NullPointerException)。推导:若函数参数类型为 String,任何调用点都不可能传入 null(编译期拒绝),从而消灭了一整类运行时异常。

智能转换的成立条件:目标变量在检查点后未被修改且不是开放属性(open member),编译器才允许自动转换类型。var 在并发场景可能被修改,因此智能转换受限。

3.2 val 与不可变性

val 约束的是“引用”,不是“对象”。val list = mutableListOf<Int>() 后可以 list.add(1),因为对象本身可变。Kotlin 标准库刻意区分可变与只读集合接口(MutableList vs List),用类型系统表达可变性边界。

3.3 when 的表达式语义

when 作表达式时必须覆盖所有分支(或存在 else),因为表达式的类型是各分支的公共超类型。这保证穷尽性(exhaustiveness),避免 Java switch 遗漏分支的静默行为。

3.4 字符串模板的编译展开

字符串模板编译为 StringBuilder.append 链或 String.format 的等价物,多段拼接的性能优于手工 + 链(减少中间字符串对象)。${} 内的表达式在求值时若含可空值,字符串结果为 "null" 文本(与 Java 拼接一致)。

4. 代码示例(带详尽注释)

4.1 val 与 var

// 只读引用:初始化后不可重新赋值
val appName: String = "FANDEX"

// 可变引用:可以重新赋值
var retryCount: Int = 0
retryCount += 1

// 类型推断:编译器根据初始值推断类型
val version = "1.4.2"
val maxRetries = 3

// 顶层声明:无需类包装,可直接访问
val TOP_LEVEL_CONST = "常量"

讲解:优先使用 val,只有确实需要重新赋值时才用 var。这不仅是风格,更是把“可变性”最小化的工程原则。顶层声明简化了小工具代码,是 Kotlin 与 Java 的重要差异。

4.2 基本类型与显式转换

val anInt: Int = 100
val aLong: Long = 100L          // 后缀 L 表示 Long
val aFloat: Float = 1.5f        // 后缀 f 表示 Float
val aDouble: Double = 1.5       // 默认浮点字面量是 Double
val aBoolean: Boolean = true
val aChar: Char = 'A'

// 数值类型不隐式转换:必须显式调用 toXxx
val converted: Long = anInt.toLong()
val fromString: Int = "42".toInt()

讲解:Kotlin 禁止数值类型隐式拓宽(Int 不能直接赋给 Long),避免 Java 中 int 与 long 混用的隐蔽溢出。显式转换让意图清晰,代价是少量样板。

4.3 字符串模板

val user = "Alice"
val score = 95

// 简单变量插入
val greeting = "Hello, $user!"

// 表达式插入:需要运算或方法调用时使用花括号
val report = "成绩:${score} 分,等级:${if (score >= 90) "A" else "B"}"

// 美元符号转义
val price = "单价:\$10"

讲解:字符串模板是 Kotlin 最常用的特性之一。${} 内可以是任意表达式,甚至嵌套 if。转义 \$ 避免与模板语法冲突。

4.4 if 与 when 表达式

// if 作为表达式:直接赋值
val max = if (a > b) a else b

// when 作为表达式:多分支匹配
val grade = when (score) {
    in 90..100 -> "优秀"
    in 80..89 -> "良好"
    in 60..79 -> "及格"
    else -> "不及格"
}

// when 无参数形式:替代 if-else 链
val result = when {
    score >= 90 -> "优秀"
    score >= 60 -> "通过"
    else -> "未通过"
}

讲解:when 的 in 分支使用区间匹配;作为表达式时必须穷尽(有 else)。无参数 when 适合多个互斥条件判断,可读性优于嵌套 if。

4.5 循环与区间

// 闭区间:0 到 5 包含 5
for (i in 0..5) {
    println(i)
}

// 半开区间:0 到 4
for (i in 0 until 5) {
    println(i)
}

// 递减 + 步长
for (i in 10 downTo 1 step 2) {
    println(i)
}

// 遍历集合
val names = listOf("Kotlin", "Java", "Go")
for (name in names) {
    println(name)
}

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

讲解:区间与 for 的组合覆盖绝大多数迭代需求。withIndex() 解构出索引与元素,避免手动维护计数器。downTo 与 step 让倒序步进循环声明式化。

4.6 类型检查与安全转换

fun describe(value: Any): String {
    // is 检查 + 智能转换:分支内 value 自动变为 String
    if (value is String) {
        return "字符串,长度 ${value.length}"
    }
    // 智能转换对不可变局部变量有效
    if (value is Int) {
        return "整数 ${value + 1}"
    }
    return "未知类型"
}

// 安全转换:失败返回 null 而不是抛异常
val number: Int? = "123".toIntOrNull()

// as? 安全强转
val text: String? = value as? String

讲解:is 配合智能转换是 Kotlin 类型系统的招牌能力;toIntOrNull 与 as? 让“可能失败”的转换返回可空结果,由调用方处理,而不是抛异常。

4.7 空安全操作符

data class User(val name: String?, val email: String?)

fun format(user: User?): String {
    // 安全调用:user 为 null 时整链为 null
    val upperName = user?.name?.uppercase()

    // Elvis:为 null 时使用默认值
    val displayName = user?.name ?: "匿名用户"

    // 链式组合:安全调用 + Elvis 提供完整默认
    val email = user?.email ?: "未提供邮箱"

    // !! 非空断言:明确表示不可能为 null(滥用会重新引入 NPE)
    // val dangerous = user!!.name

    return "$displayName($email)"
}

// 调用:传入 null 也不会崩溃
println(format(null))
println(format(User(null, "alice@example.com")))

讲解:?.、?: 组合是 Kotlin 空安全的标准模式。!! 是逃生舱,仅用于“与 Java 互操作且确定非空”的场景;业务代码中应尽量避免。

4.8 包与导入

package com.fandex.tools

// 导入单个声明
import kotlin.math.sqrt

// 通配导入
import java.time.*

// 别名:解决命名冲突
import java.util.Date as JavaDate

讲解:Kotlin 的包与导入机制与 Java 类似,但增加 as 别名解决冲突,支持顶层声明直接导入。目录结构与包名不必强一致(但建议一致以利维护)。

5. 对比分析

5.1 Kotlin 与 Java 基础语法对比

维度KotlinJava
变量val/var + 推断类型前置,无推断(局部可 var)
空安全类型系统内置注解可选,运行时检查
字符串模板原生无(需拼接或 format)
switchwhen 表达式switch 语句(14+ 有表达式)
区间.. until downTo step无内置
智能转换是无(Java 16 模式匹配部分实现)

5.2 val 与 Java final

val 等价于 Java final 局部变量;但 Kotlin 的只读集合接口(List)是更深层的不可变约束,Java 的 Collections.unmodifiableList 是运行时包装。

5.3 可空类型与 Optional

Java 8 的 Optional 是包装类型,有装箱开销且不能用于字段;Kotlin 的可空性是类型系统特性,无运行时开销。在互操作边界(Java 调用 Kotlin),可空性通过 @Nullable/@NotNull 注解导出。

6. 常见陷阱与最佳实践

陷阱一:把 val 当作不可变对象。val 只约束引用;需要不可变数据时使用 data class + 只读集合。

陷阱二:滥用 !! 导致 NPE 回潮。最佳实践:用 ?:、?.、toIntOrNull 等安全手段;!! 只在互操作边界使用。

陷阱三:数值隐式转换的直觉错误。Int 与 Long 运算必须先显式转换,否则编译失败(这是特性不是 bug)。

陷阱四:字符串模板中 $ 未转义。需要输出美元符号时写 \$。

陷阱五:when 表达式缺少 else 分支导致编译错误。作为表达式时必须穷尽。

陷阱六:智能转换在 var 或并发修改下失效。改用局部 val 副本或显式转换。

陷阱七:把 Kotlin 源码放在错误目录或包名不匹配,IDE 能纠正但命令行构建失败。保持目录与包一致。

最佳实践:默认 val;空安全用 ?./?:;when 优先于 if-else 链;区间循环优先于索引循环;每个函数保持小且纯。

7. 工程实践

7.1 项目结构

src/main/kotlin/
  com/fandex/app/
    Main.kt          # 入口:main 函数
    model/           # 数据类
    service/         # 业务逻辑
    util/            # 扩展函数与工具
src/test/kotlin/
  com/fandex/app/
    MainTest.kt      # 单元测试

讲解:Kotlin 项目结构与 Java 类似,但顶层函数减少了“工具类”的样板。测试目录镜像主目录,使用 kotlin.test 或 JUnit 5。

7.2 构建工具

// build.gradle.kts:Kotlin DSL
plugins {
    kotlin("jvm") version "2.1.20"
    application
}

repositories {
    mavenCentral()
}

dependencies {
    testImplementation(kotlin("test"))
}

application {
    mainClass.set("com.fandex.app.MainKt")
}

讲解:Gradle Kotlin DSL 是 Kotlin 项目的主流构建方式,构建脚本本身也是 Kotlin 代码,获得类型检查与 IDE 补全。mainClass 指向 MainKt(顶层 main 函数所在文件的 JVM 类名)。

7.3 与 Java 互操作

// 调用 Java 代码:直接使用
val list = java.util.ArrayList<String>()
list.add("Kotlin")

// Java 平台类型(String!)需要自行决定可空处理
val maybeNull: String? = javaMethodMayReturnNull()

讲解:Kotlin 可以无缝调用 Java API;Java 返回的类型是“平台类型”,编译器不强制可空检查,需要开发者根据上下文处理。

8. 案例研究:学生成绩统计工具

需求:读取成绩列表,计算平均分、最高分、等级分布,并以表格形式输出。用基础语法完整实现:

data class Student(val name: String, val score: Int)

fun main() {
    val students = listOf(
        Student("Alice", 92),
        Student("Bob", 78),
        Student("Carol", 85),
        Student("Dave", 59)
    )

    // 平均分:sum 与 count 组合
    val average = students.map { it.score }.average()
    println("平均分:%.1f".format(average))

    // 最高分与姓名
    val top = students.maxByOrNull { it.score }
    println("最高分:${top?.name}(${top?.score})")

    // 等级分布:groupBy + 区间判断
    val byGrade = students.groupBy { student ->
        when (student.score) {
            in 90..100 -> "优秀"
            in 80..89 -> "良好"
            in 60..79 -> "及格"
            else -> "不及格"
        }
    }

    // 输出表格
    byGrade.forEach { (grade, list) ->
        println("$grade:${list.size} 人 - ${list.joinToString { it.name }}")
    }
}

讲解:该案例综合使用 data class、集合操作(map、maxByOrNull、groupBy)、when 表达式、字符串模板与 ?. 空安全。输出:

平均分:78.5;最高分:Alice(92);优秀:1 人 - Alice;良好:1 人 - Carol;及格:1 人 - Bob;不及格:1 人 - Dave。

9. 知识要点总结与深入讲解

Kotlin 基础语法的设计哲学可以概括为“表达力优先、安全内建”:val/var 表达可变性意图,空安全表达失败可能,when/区间/模板减少样板。每学一个特性,都应与 Java 对照理解“解决的是什么痛点”。

空安全的三个操作符是递进关系:?. 传播空、?: 提供默认、!! 断言非空。工程上 !! 越少越好,互操作边界之外几乎可以消除 NPE。

类型推断不是类型弱化:Kotlin 仍是强静态类型语言,推断发生在编译期。理解了这一点,就不会误以为 val x = 1 是动态类型。

1. 变量声明

Kotlin 提供两种变量声明方式:val(只读)和 var(可变)。

1.1 val(只读变量)

val name: String = "Kotlin"  // 显式类型
val version = 2.2            // 类型推断为 Double
val year = 2011              // 类型推断为 Int

// name = "Java"  // 编译错误:Val cannot be reassigned

最佳实践:优先使用 val,仅在确实需要修改变量时才使用 var。这使代码更安全、更易推理。

1.2 var(可变变量)

var count: Int = 0
count = 1           // OK
count += 10         // OK

var message = "Hello"
message = "World"   // OK,类型必须一致
// message = 42     // 编译错误:Type mismatch

1.3 延迟初始化

// lateinit — 用于 var,延迟初始化引用类型
lateinit var service: UserService

fun setup() {
    service = UserService()  // 在使用前初始化
}

// by lazy — 用于 val,首次访问时初始化
val heavyObject: ExpensiveClass by lazy {
    println("Initializing...")
    ExpensiveClass()
}

1.4 常量

// 编译期常量(顶层或伴生对象中)
const val MAX_SIZE = 100
const val APP_NAME = "FANDEX"

// 运行时常量
val runtimeConstant = computeValue()

1. 基本类型

与 Java 不同,Kotlin 中一切皆对象,基本类型在可能时编译为 Java 原始类型。

1.1 数值类型

类型位数最小值最大值
Byte8-128127
Short16-3276832767
Int32-2³¹2³¹-1
Long64-2⁶³2⁶³-1
Float32IEEE 754IEEE 754
Double64IEEE 754IEEE 754
val intVal = 42              // Int
val longVal = 42L            // Long
val doubleVal = 3.14         // Double
val floatVal = 3.14f         // Float
val hexVal = 0xFF            // Int (十六进制)
val binaryVal = 0b1010       // Int (二进制)
val underscored = 1_000_000  // Int (下划线分隔,提高可读性)

// 数值转换 — Kotlin 不支持隐式转换
val intVal2: Int = 100
val longVal2: Long = intVal2.toLong()   // 显式转换
val doubleVal2: Double = intVal2.toDouble()

1.2 布尔类型

val isActive: Boolean = true
val isComplete = false

// 惰性逻辑运算
val result = isActive && expensiveCheck()  // 短路求值

1.3 字符与字符串

// Char — 用单引号
val letter: Char = 'A'
val unicode: Char = '\u0041'  // 'A'

// String — 用双引号
val text: String = "Hello, Kotlin"

// 原始字符串(三引号)— 保留格式
val rawText = """
    |Hello,
    |Kotlin!
""".trimMargin()  // trimMargin 去除 | 前缀

val rawText2 = """
    Hello,
    Kotlin!
""".trimIndent()  // 去除公共缩进

1.4 数组

// 创建数组
val numbers = arrayOf(1, 2, 3, 4, 5)           // Array<Int>
val strings = arrayOf("a", "b", "c")            // Array<String>
val mixed = arrayOf(1, "two", 3.0)              // Array<Any>

// 原始类型数组(无装箱开销)
val intArray = intArrayOf(1, 2, 3)              // IntArray
val byteArray = byteArrayOf(1, 2, 3)            // ByteArray
val longArray = longArrayOf(1L, 2L, 3L)         // LongArray

// 构造函数创建
val squares = Array(5) { i -> i * i }           // [0, 1, 4, 9, 16]
val zeros = IntArray(5)                          // [0, 0, 0, 0, 0]
val ones = IntArray(5) { 1 }                    // [1, 1, 1, 1, 1]

2. 字符串模板

Kotlin 支持字符串模板,比 Java 的字符串拼接更简洁高效:

val name = "Kotlin"
val version = 2.2

// 简单模板
println("Language: $name")                    // Language: Kotlin

// 表达式模板
println("Version: ${version + 0.1}")          // Version: 2.3

// 嵌套表达式
val list = listOf("a", "b", "c")
println("Size: ${list.size}, First: ${list[0]}")  // Size: 3, First: a

// 在原始字符串中使用
val json = """
    {
        "name": "$name",
        "version": $version
    }
""".trimIndent()

注意:如果需要在字符串中使用 $ 字面量,需要转义:${'$'} 或 \$。

3. 包与导入

3.1 包声明

package com.example.kotlinbasics

// 文件中的所有声明都属于此包
class MyClass
fun topLevelFunction() = "Hello"

3.2 导入

// 默认导入(无需显式声明)
// kotlin.*、kotlin.annotation.*、kotlin.collections.* 等

// 显式导入
import com.example.utils.Logger
import com.example.utils.formatDate

// 导入并重命名(解决冲突)
import com.example.utils.formatDate as formatDateUtil
import com.other.utils.formatDate as formatDateOther

// 导入整个包
import com.example.utils.*

// 导入伴生对象成员
import com.example.Config.DEFAULT_TIMEOUT

4. 控制流

4.1 if 表达式

Kotlin 中 if 是表达式,有返回值:

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

// 多行 if 表达式
val result = if (score >= 90) {
    println("Excellent")
    "A"
} else if (score >= 80) {
    println("Good")
    "B"
} else {
    println("Keep going")
    "C"
}

4.2 when 表达式

when 是 Kotlin 中强大的模式匹配工具,替代 Java 的 switch:

// 基本 when
when (x) {
    1 -> println("One")
    2, 3 -> println("Two or Three")
    in 4..10 -> println("Four to Ten")
    !in 11..20 -> println("Not in 11-20")
    is String -> println("It's a String")
    else -> println("Unknown")
}

// when 作为表达式
val description = when (x) {
    0 -> "Zero"
    1, 2, 3 -> "Small"
    in 4..100 -> "Medium"
    else -> "Large"
}

// 无参 when(替代 if-else 链)
when {
    x > 0 -> println("Positive")
    x < 0 -> println("Negative")
    else -> println("Zero")
}

// 捕获 when 主体中的变量
fun process(input: Any) = when (input) {
    is Int -> "Integer: ${input * 2}"    // input smart-cast to Int
    is String -> "String of length ${input.length}"
    is List<*> -> "List with ${input.size} elements"
    else -> "Unknown type"
}

4.3 for 循环

// 遍历区间
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 items = listOf("apple", "banana", "cherry")
for (item in items) println(item)

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

// 遍历 Map
val map = mapOf("a" to 1, "b" to 2, "c" to 3)
for ((key, value) in map) {
    println("$key = $value")
}

4.4 while 与 do-while

var i = 0
while (i < 5) {
    println(i)
    i++
}

var input: String
do {
    input = readLine() ?: ""
} while (input.isEmpty())

4.5 循环控制

// break 和 continue
for (i in 1..10) {
    if (i == 3) continue  // 跳过 3
    if (i == 7) break     // 到 7 停止
    println(i)
}

// 标签循环
loop@ for (i in 1..5) {
    for (j in 1..5) {
        if (i * j == 6) break@loop  // 跳出外层循环
        println("$i * $j = ${i * j}")
    }
}

5. 区间与数列

5.1 区间(Range)

// 闭区间
val range1 = 1..10        // IntRange: 1, 2, ..., 10
val range2 = 'a'..'z'     // CharRange: a, b, ..., z

// 半开区间
val range3 = 1 until 10   // IntRange: 1, 2, ..., 9

// 递减区间
val range4 = 10 downTo 1  // IntRange: 10, 9, ..., 1

// 带步长
val range5 = 1..10 step 2  // 1, 3, 5, 7, 9
val range6 = 10 downTo 1 step 3  // 10, 7, 4, 1

5.2 区间操作

val range = 1..100

// 包含检查
3 in range          // true
200 in range        // false
50 !in range        // false

// 区间判断
val ch = 'k'
ch in 'a'..'z'      // true
ch in 'A'..'Z'      // false

// 实用函数
(1..10).random()    // 随机数
(1..10).first       // 1
(1..10).last        // 10
(1..10).step(3)     // 1, 4, 7, 10

5.3 数列(Progression)

区间本质上是数列的实现,数列定义了 first、last 和 step:

// 自定义步长的数列
val progression = IntProgression.fromClosedRange(1, 10, 3)
// 1, 4, 7, 10

// 数列转列表
val list = (1..10 step 2).toList()  // [1, 3, 5, 7, 9]

6. 类型检查与转换

// is 和 !is 操作符
if (obj is String) {
    // obj 在此分支自动智能转换为 String
    println(obj.length)
}

// as 和 as? 类型转换
val x: Any = "Hello"
val s1: String = x as String       // 不安全转换,可能抛出 ClassCastException
val s2: String? = x as? String     // 安全转换,失败返回 null
val s3: Int? = x as? Int           // null(转换失败)

智能转换是 Kotlin 的核心特性之一。编译器在条件分支中自动进行类型转换,无需手动强转,既安全又简洁。

变量声明

基本写法:val 声明显式类型只读变量 val <name>: <Type> = <value>

// 声明显式类型的只读变量
val name: String = "Kotlin";

基本写法:val 类型推断只读变量 val <name> = <value>

// 类型推断为 Double
val version = 2.2;
// 类型推断为 Int
val year = 2011;

基本写法:var 声明显式类型可变变量 var <name>: <Type> = <value>

// 声明可变变量并修改
var count: Int = 0;
count = 1;
count += 10;

基本写法:var 类型推断可变变量 var <name> = <value>

// 声明可变字符串变量
var message = "Hello";
message = "World";

基本写法:lateinit 延迟初始化可变变量 lateinit var <name>: <Type>

// 用于 var,延迟初始化引用类型
lateinit var service: UserService;
fun setup() {
    service = UserService();
}

基本写法:by lazy 首次访问时初始化只读变量 val <name>: <Type> by lazy { <init> }

// 用于 val,首次访问时初始化
val heavyObject: ExpensiveClass by lazy {
    println("Initializing...");
    ExpensiveClass();
}

基本写法:const val 编译期常量 const val <name> = <value>

// 编译期常量(顶层或伴生对象中)
const val MAX_SIZE = 100;

单行写法:const val 多常量声明 const val <name1> = <value1>; const val <name2> = <value2>

// 单行声明多个编译期常量
const val APP_NAME = "FANDEX"; const val VERSION = "1.0";

基本类型

基本写法:Int 整数字面量 val <name> = <int>

// Int 类型字面量
val intVal = 42;

基本写法:Long 长整数字面量 val <name> = <int>L

// Long 类型字面量(后缀 L)
val longVal = 42L;

基本写法:Double 双精度浮点字面量 val <name> = <float>

// Double 类型字面量
val doubleVal = 3.14;

基本写法:Float 单精度浮点字面量 val <name> = <float>f

// Float 类型字面量(后缀 f)
val floatVal = 3.14f;

基本写法:十六进制字面量 val <name> = 0x<hex>

// 十六进制 Int 字面量
val hexVal = 0xFF;

基本写法:二进制字面量 val <name> = 0b<binary>

// 二进制 Int 字面量
val binaryVal = 0b1010;

基本写法:下划线分隔字面量 val <name> = <int_with_underscores>

// 下划线提升可读性
val underscored = 1_000_000;

基本写法:数值显式转换 <value>.to<Type>()

// Kotlin 不支持隐式转换,必须显式调用转换函数
val intVal: Int = 100;
val longVal: Long = intVal.toLong();

基本写法:Boolean 布尔类型 val <name>: Boolean = <bool>

// 声明布尔变量
val isActive: Boolean = true;

基本写法:短路求值 val <name> = <bool> && <expr>

// 短路求值,expensiveCheck 在 isActive 为 false 时不执行
val result = isActive && expensiveCheck();

基本写法:Char 字符类型 val <name>: Char = '<char>'

// Char 用单引号
val letter: Char = 'A';

基本写法:Char Unicode 字符 val <name>: Char = '\u<hex>'

// Unicode 字符
val unicode: Char = '\u0041';

基本写法:String 字符串类型 val <name>: String = "<text>"

// String 用双引号
val text: String = "Hello, Kotlin";

单行写法:trimMargin 原始字符串 """<content>""".trimMargin()

// trimMargin 去除 | 前缀
val rawText = """
    |Hello,
    |Kotlin!
""".trimMargin();

单行写法:trimIndent 原始字符串 """<content>""".trimIndent()

// trimIndent 去除公共缩进
val rawText2 = """
    Hello,
    Kotlin!
""".trimIndent();

单行写法:arrayOf 创建对象数组 arrayOf(<elements>)

// 对象数组
val numbers = arrayOf(1, 2, 3, 4, 5);

单行写法:intArrayOf 创建原始类型数组 <type>ArrayOf(<elements>)

// 原始类型数组(无装箱开销)
val intArray = intArrayOf(1, 2, 3);

换行写法:Array 构造函数创建数组 Array(<size>) { <index> -> <expr> }

// 构造函数创建数组,按索引计算元素
val squares = Array(5) { i -> i * i };

换行写法:IntArray 构造函数创建数组 <Type>Array(<size>) { <init> }

// 构造函数创建原始类型数组并初始化
val ones = IntArray(5) { 1 };

字符串模板

基本写法:简单变量模板 "...$<name>..."

// 简单模板:直接插入变量
val name = "Kotlin";
println("Language: $name");

基本写法:表达式模板 "...${<expression>}..."

// 表达式模板:插入计算结果
val version = 2.2;
println("Version: ${version + 0.1}");

基本写法:嵌套表达式模板 "...${<obj>.<prop>}..."

// 嵌套表达式:访问属性
val list = listOf("a", "b", "c");
println("Size: ${list.size}, First: ${list[0]}");

单行写法:原始字符串中使用模板 """...$<name>..."""

// 原始字符串中使用模板
val name = "Kotlin";
val json = """
    {
        "name": "$name"
    }
""".trimIndent();

包与导入

基本写法:包声明 package <package.name>

// 声明包名
package com.example.kotlinbasics;

基本写法:显式导入 import <package>.<name>

// 显式导入单个类或函数
import com.example.utils.Logger;

基本写法:导入并重命名 import <package>.<name> as <alias>

// 导入并重命名解决冲突
import com.example.utils.formatDate as formatDateUtil;

基本写法:导入整个包 import <package>.*

// 导入整个包的所有内容
import com.example.utils.*;

基本写法:导入伴生对象成员 import <package>.<Class>.<member>

// 导入伴生对象成员
import com.example.Config.DEFAULT_TIMEOUT;

控制流

基本写法:if 表达式 val <name> = if (<cond>) <exprA> else <exprB>

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

换行写法:多分支 if 表达式 val <name> = if (<cond>) { <body> } else if (<cond>) { <body> } else { <body> }

// 多行 if 表达式
val result = if (score >= 90) {
    println("Excellent");
    "A";
} else if (score >= 80) {
    println("Good");
    "B";
} else {
    println("Keep going");
    "C";
}

基本写法:when 表达式 when (<subject>) { <branches> }

// 基本 when 分支匹配
when (x) {
    1 -> println("One");
    2, 3 -> println("Two or Three");
    else -> println("Unknown");
}

基本写法:when 区间匹配 when (<subject>) { in <range> -> <expr> }

// 区间匹配
when (x) {
    in 4..10 -> println("Four to Ten");
    !in 11..20 -> println("Not in 11-20");
}

基本写法:when 类型匹配 when (<subject>) { is <Type> -> <expr> }

// 类型匹配
when (x) {
    is String -> println("It's a String");
}

基本写法:when 作为表达式赋值 val <name> = when (<subject>) { <branches> }

// when 表达式返回值
val description = when (x) {
    0 -> "Zero";
    1, 2, 3 -> "Small";
    in 4..100 -> "Medium";
    else -> "Large";
}

基本写法:无参 when when { <branches> }

// 无参 when 替代 if-else 链
when {
    x > 0 -> println("Positive");
    x < 0 -> println("Negative");
    else -> println("Zero");
}

基本写法:when 捕获变量智能转换 fun <name>(<param>: Any) = when (<param>) { is <Type> -> <expr> }

// when 中 is 检查后智能转换
fun process(input: Any) = when (input) {
    is Int -> "Integer: ${input * 2}";
    is String -> "String of length ${input.length}";
    else -> "Unknown type";
}

基本写法:for 遍历闭区间 for (<item> in <start>..<end>) { <body> }

// 遍历闭区间
for (i in 1..5) print("$i ");

基本写法:for 遍历半开区间 for (<item> in <start> until <end>) { <body> }

// 遍历半开区间(排除末尾)
for (i in 1 until 5) print("$i ");

基本写法:for 递减遍历 for (<item> in <start> downTo <end>) { <body> }

// 递减遍历
for (i in 5 downTo 1) print("$i ");

基本写法:for 带步长遍历 for (<item> in <range> step <n>) { <body> }

// 带步长遍历
for (i in 1..10 step 2) print("$i ");

基本写法:for 遍历集合 for (<item> in <iterable>) { <body> }

// 遍历集合元素
val items = listOf("apple", "banana", "cherry");
for (item in items) println(item);

基本写法:带索引遍历 for ((<index>, <value>) in <collection>.withIndex()) { <body> }

// 带索引遍历集合
for ((index, value) in items.withIndex()) {
    println("$index: $value");
}

基本写法:遍历 Map for ((<key>, <value>) in <map>) { <body> }

// 遍历 Map 键值对
val map = mapOf("a" to 1, "b" to 2);
for ((key, value) in map) {
    println("$key = $value");
}

基本写法:while 循环 while (<cond>) { <body> }

// while 循环
var i = 0;
while (i < 5) {
    println(i);
    i++;
}

基本写法:do-while 循环 do { <body> } while (<cond>)

// do-while 循环(至少执行一次)
var input: String;
do {
    input = readLine() ?: "";
} while (input.isEmpty());

基本写法:break 跳出循环 break

// break 跳出循环
for (i in 1..10) {
    if (i == 7) break;
    println(i);
}

基本写法:continue 跳过本次 continue

// continue 跳过当前迭代
for (i in 1..10) {
    if (i == 3) continue;
    println(i);
}

基本写法:标签 break break@<label>

// 标签 break 跳出外层循环
loop@ for (i in 1..5) {
    for (j in 1..5) {
        if (i * j == 6) break@loop;
        println("$i * $j = ${i * j}");
    }
}

区间与数列

基本写法:闭区间 <start>..<end>

// 闭区间:包含 end
val range1 = 1..10;

基本写法:字符区间 '<char>'..'<char>'

// 字符区间
val range2 = 'a'..'z';

基本写法:半开区间 <start> until <end>

// 半开区间:不包含 end
val range3 = 1 until 10;

基本写法:递减区间 <start> downTo <end>

// 递减区间
val range4 = 10 downTo 1;

基本写法:带步长区间 <range> step <n>

// 带步长的区间
val range5 = 1..10 step 2;

基本写法:递减带步长区间 <start> downTo <end> step <n>

// 递减且带步长
val range6 = 10 downTo 1 step 3;

基本写法:in 包含检查 <value> in <range>

// 检查值是否在区间内
val range = 1..100;
3 in range;

基本写法:!in 不包含检查 <value> !in <range>

// 检查值是否不在区间内
50 !in range;

基本写法:区间随机数 <range>.random()

// 从区间获取随机数
(1..10).random();

基本写法:区间首尾属性 <range>.[first|last]

// 获取区间首尾元素
(1..10).first;
(1..10).last;

基本写法:数列自定义步长 IntProgression.fromClosedRange(<start>, <end>, <step>)

// 自定义步长的数列
val progression = IntProgression.fromClosedRange(1, 10, 3);

基本写法:区间转列表 <range>.toList()

// 区间转换为列表
val list = (1..10 step 2).toList();

类型检查与转换

基本写法:is 类型检查 if (<obj> is <Type>) { <body> }

// is 检查后智能转换
if (obj is String) {
    println(obj.length);
}

基本写法:!is 类型检查 if (<obj> !is <Type>) { <body> }

// !is 检查类型不匹配
if (obj !is String) {
    println("Not a String");
}

基本写法:as 不安全类型转换 <obj> as <Type>

// 不安全转换,可能抛出 ClassCastException
val x: Any = "Hello";
val s1: String = x as String;

基本写法:as? 安全类型转换 <obj> as? <Type>

// 安全转换,失败返回 null
val s2: String? = x as? String;

Kotlin 2.x 新特性

基本写法:Kotlin 2.0 K2 编译器前端 // 默认启用 K2 编译器

// Kotlin 2.0 起默认启用 K2 编译器前端
// 无需额外配置,编译速度与稳定性显著提升
// 旧版本手动启用:在 gradle.properties 中设置 kotlin.language.version=2.0
fun main() {
    // K2 编译器对类型推断、内联函数处理更精确
    val list = listOf(1, 2, 3).map { it * 2 }
    println(list)
}

基本写法:Kotlin 2.1 guard 条件 in when when (<x>) { <条件> && guard -> <语句> }

// when 分支支持 guard 条件,使用 if 关键字附加布尔守卫
fun classify(x: Any): String = when (x) {
    is Int if x > 0 -> "正整数"
    is Int if x < 0 -> "负整数"
    is Int -> "零"
    is String if x.isNotEmpty() -> "非空字符串"
    else -> "其他"
}
// 调用示例
println(classify(42))       // 正整数
println(classify("hello")) // 非空字符串

基本写法:Kotlin 2.1 多重赋值 val (<a>, <b>) = <pair>

// 多重赋值:解构 Pair/Triple 到多个变量
val pair = "Alice" to 30
val (name, age) = pair
println("$name $age") // Alice 30
// 用于函数返回多值场景
fun userInfo(): Pair<String, Int> = "Bob" to 25
val (n, a) = userInfo()
// 也支持 List 解构
val (first, second) = listOf(10, 20)

基本写法:Kotlin 2.2 context parameters(上下文参数) context(<参数>) fun <name>() { }

// 上下文参数:声明函数隐式依赖的上下文值
// 注:更早的实验性 "context receivers" 设计已被其取代并废弃
class Logger { fun log(msg: String) { println(msg) } }
class Config { val env = "prod" }
// 函数同时依赖 Logger 和 Config 两个上下文
context(Logger, Config)
fun printEnv() {
    log("当前环境: $env")
}
// 调用时需在对应接收者作用域内
with(Logger()) {
    with(Config()) {
        printEnv()
    }
}

基本写法:Kotlin 2.3 Java 25 互操作增强 // Kotlin 2.3 增强 Java 25 互操作

// Kotlin 2.3 改进与 Java 25 新特性的互操作
// 支持 Java 25 模式匹配、记录类、密封类等特性的更优调用
// Java 25 密封类可在 Kotlin 中直接 when 穷尽匹配
sealed interface JShape permits JCircle, JSquare {}
// Kotlin 中无 else 分支也能穷尽匹配
fun area(s: JShape): Double = when (s) {
    is JCircle -> Math.PI * s.r * s.r
    is JSquare -> s.side * s.side
}