基础
变量与类型
优先使用 val(不可变)而非 var(可变)。Kotlin 会进行类型推断但允许显式标注。使用 'is' 进行类型检查(自动智能转换)。一切都是对象——Kotlin 语法中没有原始类型。
val name = "Alice" // immutable (preferred)
var age = 30 // mutable
val pi: Double = 3.14159
val isDev: Boolean = true
val nums: List<Int> = listOf(1, 2, 3)
println(name::class) // class kotlin.String
println(age is Int) // true字符串模板
$var 用于简单变量,${expr} 用于表达式。三引号字符串保留换行——使用 trimIndent() 获得整洁格式。字符串模板使字符串构建简洁可读,无需格式化字符串。
val name = "Alice"
val age = 30
println("Name: $name, Age: $age") // simple var
println("Length: ${name.length}") // expression
println("Upper: ${name.uppercase()}")
val multiline = """
Hello, $name
Age: $age
""".trimIndent()空安全
Kotlin 的空安全:? 标记可空类型,?. 安全调用(为 null 时返回 null),!! 断言非空(为 null 时抛出 NPE),?: elvis(为 null 时的默认值)。这在编译时消除了 NullPointerException——Kotlin 的核心特性。
var name: String? = null // nullable type
println(name?.length) // null (safe call)
name = "Alice"
println(name!!.length) // 5 (non-null assertion)
val len: Int = name?.length ?: 0 // elvis operator
// Non-null types can never be null
val s: String = "hi" // not nullable类型转换
Kotlin 要求显式类型转换(没有 Java 那样的隐式拓宽)。使用 toInt()/toDouble()/toString() 进行转换。toIntOrNull() 在失败时返回 null——比抛出异常的 toInt() 更安全。始终处理解析中的 null。
val n: Int = 42
val d: Double = n.toDouble()
val s: String = n.toString()
val fromStr: Int? = "100".toIntOrNull()
val invalid: Int? = "abc".toIntOrNull() // null
println(d, s, fromStr, invalid)类型检查与智能转换
is 检查类型并在该分支中智能转换变量——无需显式转换。!is 是否定形式。使用 as 进行不安全转换(抛出 ClassCastException),as? 进行安全转换(失败时返回 null)。智能转换是 Kotlin 的一大便利。
fun describe(x: Any): String =
when (x) {
is Int -> "Int: ${x + 1}" // x smart-cast to Int
is String -> "String of length ${x.length}"
is List<*> -> "List with ${x.size} items"
else -> "Unknown"
}
println(describe(42)) // Int: 43
println(describe("hello")) // String of length 5字符串
常用字符串方 法
Kotlin 字符串拥有标准库提供的丰富扩展函数。大多数返回新字符串(不可变)。使用这些替代手动循环。uppercase() 等函数是区域感知的——使用 uppercase(Locale.ROOT) 获得一致结果。
val s = "Hello, World"
println(s.length) // 12
println(s.uppercase()) // HELLO, WORLD
println(s.lowercase()) // hello, world
println(s.split(", ")) // [Hello, World]
println(s.replace("o", "0")) // Hell0, W0rld
println(s.reversed()) // dlroW ,olleH
println(s.startsWith("Hello")) // true多行与原始字符串
三引号字符串是原始的——无需转义序列(三重双引号除外)。trimMargin('|') 只保留 | 之后的文本以获得整洁缩进。trimIndent() 移除公共空白。非常适合代码中的 SQL、JSON、HTML。
val text = """
|Hello,
|World!
""".trimMargin() // | marks line start
println(text)
val json = """{"name": "Alice", "age": 30}"""
println(json) // no escape needed for quotes
// trimIndent() removes common leading whitespace字符串构建
joinToString 是用分隔符、前缀、后缀连接集合的地道方式。buildString 提供 StringBuilder 作用域以简洁构建。避免在循环中重复使用 +——使用 joinToString 或 StringBuilder 以提高效率。
val parts = listOf("apple", "banana", "cherry")
println(parts.joinToString(", ")) // apple, banana, cherry
println(parts.joinToString(prefix="[", postfix="]"))
val sb = StringBuilder()
for (p in parts) sb.append(p).append(" ")
println(sb.toString().trim())
// buildString for concise building
val result = buildString {
parts.forEach { append(it).append(";") }
}字符串转数字
toInt()/toDouble() 在无效输入时抛出 NumberFormatException。toIntOrNull() 返回 null——配合 ?: 获得安全默认值。toBooleanStrict() 只接受 'true'/'false'。对用户输入或不可信数据使用 OrNull 变体。
val n = "42".toInt() // 42 (throws if invalid)
val d = "3.14".toDouble() // 3.14
val safe = "abc".toIntOrNull() // null
val withDefault = "x".toIntOrNull() ?: 0 // 0
val bool = "true".toBooleanStrict() // true
println(n, d, safe, withDefault)正则表达式
Regex 类包装 Java 的 Pattern。find 返回 MatchResult?(第一个匹配),findAll 返回所有。matches 检查完全匹配,containsMatchIn 检查部分匹配。使用三引号字符串编写模式以避免双重转义反斜杠。
val email = Regex("[\w.]+@[\w]+\.[a-z]+")
val text = "Contact: [email protected]"
val match = email.find(text)
println(match?.value) // [email protected]
println(email.matches("[email protected]")) // true
val replaced = text.replace(Regex("\d+"), "#")
println(email.containsMatchIn(text)) // true数据结构
List
listOf 创建不可变列表,mutableListOf 创建可变列表。列表从零开始索引。使用 first()/last() 获取两端(空时抛出异常),firstOrNull() 更安全。contains 检查成员。除非需要修改,否则优先使用不可变列表。
val nums = listOf(1, 2, 3) // immutable
val mutable = mutableListOf(1, 2, 3)
mutable.add(4)
mutable[0] = 0
println(nums.size) // 3
println(nums.first()) // 1
println(nums.last()) // 3
println(nums.contains(2)) // true
println(nums.indexOf(2)) // 1Map
mapOf 创建不可变映射,mutableMapOf 创建可变映射。'to' 中缀创建键值对。[] 访问返回可空的 V?(缺失时为 null)。使用 getOrDefault 或 getValue(缺失时抛出异常)。用解构 (k, v) 迭代。
val ages = mapOf("Alice" to 30, "Bob" to 25)
println(ages["Alice"]) // 30
println(ages.getOrDefault("Eve", 0)) // 0
println(ages.containsKey("Alice")) // true
for ((k, v) in ages) println("$k: $v")
val mutable = mutableMapOf("a" to 1)
mutable["b"] = 2 // add/updateSet
setOf 创建不可变集合,mutableSetOf 创建可变集合。union/intersect/subtract 返回新集合。集合强制唯一性——如果元素已存在,add 返回 false。用于去重和集合运算。LinkedHashSet 保留插入顺序。
val a = setOf(1, 2, 3)
val b = setOf(3, 4, 5)
println(a union b) // [1,2,3,4,5]
println(a intersect b) // [3]
println(a subtract b) // [1,2]
println(a.contains(2)) // true
val mutable = mutableSetOf(1, 2)
mutable.add(3) // true if addedArray 与原始数组
arrayOf 创建 Array<T>(原始类型会装箱)。使用 intArrayOf/doubleArrayOf 创建未装箱的原始数组(性能更好)。Array(size) { init } 用初始化函数创建。数组是可变的;大多数用例优先使用 List。
val arr = arrayOf(1, 2, 3) // Array<Int>
arr[0] = 10
println(arr.size) // 3
// Specialized arrays for primitives (no boxing)
val ints = intArrayOf(1, 2, 3)
val doubles = doubleArrayOf(1.0, 2.0)
// Array constructor
val squares = Array(5) { it * it } // [0,1,4,9,16]
println(squares.toList())Pair 与 Triple
Pair 和 Triple 组合 2-3 个值。'to' 中缀创建 Pair(常用于映射)。用 val (a, b) = pair 解构。对于超过 3 个值或命名字段,使用 data class——它们更清晰、更易维护。
val pair = "Alice" to 30 // Pair<String, Int>
println(pair.first) // Alice
println(pair.second) // 30
val (name, age) = pair // destructuring
val triple = Triple(1, "a", 2.0)
println(triple.first, triple.third)
// Useful for returning two values
fun minMax(list: List<Int>): Pair<Int, Int> =
Pair(list.min(), list.max())控制流
If / Else(表达式)
在 Kotlin 中,if/else 是返回值的表达式。这替代了三元运算符。两个分支必须返回兼容的类型。用于简洁的条件赋值。作为语句(无 else)时返回 Unit。
val score = 85
val grade = if (score >= 90) "A"
else if (score >= 80) "B"
else if (score >= 70) "C"
else "F"
println(grade) // B
// if returns a value—no ternary needed
// Both branches must have compatible typesWhen(Switch)
when 是 Kotlin 强大的 switch——支持字面量、范围(in)、否定(!in)、多值(逗号)和 is 检查。作为带 sealed 类型的表达式使用时必须穷尽。无主题时,它是更清晰的 if/else 链。
val n = 2
val label = when (n) {
0 -> "zero"
1, 2, 3 -> "small"
in 4..10 -> "medium"
!in 1..100 -> "out of range"
else -> "large"
}
println(label) // small
// When without subject = multi-condition if
when {
n > 0 -> println("positive")
n < 0 -> println("negative")
else -> println("zero")
}For 循环与范围
.. 是包含范围,until 是不包含,downTo 递减,step 设置步长。withIndex() 提供索引迭代。for-in 适用于任何 Iterable。这些范围对计数循环简洁可读。
for (i in 0 until 5) print(i) // 01234 (excludes 5)
for (i in 1..5) print(i) // 12345 (includes 5)
for (i in 5 downTo 1) print(i) // 54321
for (i in 1..10 step 2) print(i) // 13579
val list = listOf("a", "b", "c")
for ((index, value) in list.withIndex()) {
println("$index: $value")
}While 与 Do-While
while 先检查,do-while 后检查(至少运行一次)。两者都需要可变状态。优先使用 for 循环或函数式操作(map、filter)以获得不可变性。在计数未知时使用 while 进行条件驱动循环。
var count = 0
while (count < 3) {
println(count)
count++
}
var j = 0
do {
println(j)
j++
} while (j < 3)
// do-while runs at least once
// Prefer for loops when possibleBreak 与 Continue(标签)
continue 跳到下一次迭代,break 退出循环。标签(@outer)允许跳出嵌套循环——在循环前用 label@ 定义,用 @label break/continue。谨慎使用;重构为函数通常更干净。
for (i in 1..5) {
if (i == 3) continue // skip 3
if (i == 5) break // stop at 5
println(i) // 1, 2, 4
}
// Labeled breaks for nested loops
outer@ for (i in 1..3) {
for (j in 1..3) {
if (i == 2 && j == 2) break@outer
println("$i,$j")
}
}函数与 Lambda
函数定义
函数使用 fun name(params): ReturnType。单表达式用表达式体(= expr)。支持默认参数和命名参数。Unit 返回类型 = 仅副作用。默认参数减少了对重载的需求。
fun add(a: Int, b: Int): Int = a + b // expression body
fun greet(name: String, greeting: String = "Hello"): String {
return "$greeting, $name!"
}
println(add(3, 4)) // 7
println(greet("Alice")) // Hello, Alice!
println(greet("Bob", greeting = "Hi")) // named arg
fun log(msg: String): Unit = println(msg) // Unit = voidLambda 与高阶函数
Lambda:{ params -> body }。'it' 是单参数的简写。将 lambda 传递给 map/filter/reduce 等高阶函数。尾随 lambda 语法:如果最后一个参数是函数,可以放在 () 外。这是地道的 Kotlin 写法。
val square: (Int) -> Int = { x -> x * x }
println(square(5)) // 25
val nums = listOf(1, 2, 3)
println(nums.map { it * 2 }) // [2, 4, 6]
println(nums.filter { it > 1 }) // [2, 3]
println(nums.reduce { a, b -> a + b }) // 6
// 'it' is the implicit single parameter扩展函数
扩展函数为现有类(甚至来自 Java 的类)添加方法,无需继承或修改。它们是静态函数的语法糖。'this' 指向接收者。用于使 API 流畅。在编译时解析(无动态分派)。
fun String.shout(): String = this.uppercase() + "!"
fun Int.isEven(): Boolean = this % 2 == 0
println("hello".shout()) // HELLO!
println(4.isEven()) // true
// Extensions don't modify the class—they're resolved statically
// Use to add utility methods to any typeInline 与 Infix
inline 将函数体复制到调用点(消除 lambda 开销)。用于热路径中的高阶函数。infix 允许不带点/括号的函数调用(a op b)——用于 DSL 和可读操作如 'to'、'in'、'and'。
inline fun measure(block: () -> Unit) {
val start = System.currentTimeMillis()
block()
println("Took ${System.currentTimeMillis() - start}ms")
}
measure { println("working...") }
// Infix functions for readable calls
infix fun Int.times(str: String): String = str.repeat(this)
println(3 times "ab") // abababVararg 与展开
vararg 接受可变参数(收集到数组中)。* 将数组展开为 vararg。命名参数可跟在 vararg 后。用于灵活的 API 如 listOf()、printf 风格函数。展开运算符是 Kotlin 中 JS 展开的等价物。
fun sum(vararg nums: Int): Int = nums.sum()
println(sum(1, 2, 3, 4)) // 10
// Spread operator to pass array
val arr = intArrayOf(1, 2, 3)
println(sum(*arr)) // 6
// Named/default params can follow vararg
fun greet(vararg names: String, greeting: String = "Hi") =
names.joinToString { "$greeting, $it!" }
println(greet("A", "B"))类与面向对象
类与构造器
主构造器在类头中。val/var 参数成为属性(带 getter/setter)。没有 val/var 时,它们只是构造器参数(不可访问)。init 块在构造期间运行。Kotlin 自动生成 getter/setter。
class Person(val name: String, val age: Int) {
fun greet(): String = "Hi, I'm $name"
fun isAdult(): Boolean = age >= 18
}
val p = Person("Alice", 30)
println(p.greet()) // Hi, I'm Alice
println(p.name) // Alice (val = property)
println(p.isAdult()) // true
// val/var in constructor = auto propertyData Class
data class 自动生成 equals、hashCode、toString、copy 和 componentN(用于解构)。用于数据持有者。必须至少有一个 val/var 参数。copy() 创建修改后的克隆——非常适合不可变更新。用 val (a, b) = point 解构。
data class Point(val x: Int, val y: Int)
val p1 = Point(3, 4)
val p2 = Point(3, 4)
println(p1 == p2) // true (value equality)
println(p1.copy(x = 5)) // Point(x=5, y=4)
println(p1) // Point(x=3, y=4)
// Auto: equals, hashCode, toString, copy, componentN
val (x, y) = p1 // destructuring via componentN()Sealed Class
Sealed class 将子类型限制在同一文件/kotlin 模块中。when 表达式是穷尽的(编译器检查所有情况)。用于表示有限状态的 ADT(代数数据类型)。与 when 结合,实现安全的、编译时检查的模式匹配。
sealed class Result
data class Success(val value: Int) : Result()
data class Failure(val error: String) : Result()
object Loading : Result()
fun handle(r: Result): String = when (r) {
is Success -> "Got ${r.value}"
is Failure -> "Error: ${r.error}"
Loading -> "Loading..."
}
// when is exhaustive—compiler enforces all casesObject 与 Companion
object 声明单例(一个实例,懒加载初始化)。类内的 companion object 持有'静态'成员(通过 ClassName.member 访问)。使用 object 作为单例,companion 作为工厂方法和常量。Companion 可以实现接口。
object Config { // singleton
val version = "1.0"
fun load() = mapOf("key" to "value")
}
println(Config.version) // 1.0
class User(val name: String) {
companion object {
fun create(name: String) = User(name)
const val MAX = 100
}
}
val u = User.create("Alice") // like static method继承与接口
类默认是 final 的——使用 'open' 允许继承。'override' 是必需的。接口可以有默认实现。一个类继承一个类但可实现多个接 口。使用 abstract 表示部分实现。优先使用组合而非继承。
open class Animal(val name: String) {
open fun speak() = "..."
}
class Dog(name: String) : Animal(name) {
override fun speak() = "Woof"
}
interface Greetable {
val name: String
fun greet(): String // can have default impl
}
class Person(override val name: String) : Greetable {
override fun greet() = "Hi, $name"
}空安全与作用域函数
安全调用与 Elvis
?. 安全调用(接收者为 null 时返回 null),?: elvis(提供默认值)。链式 ?. 进行深层访问(user?.address?.city)。使用 ?: return/throw 提前退出。这些使 null 处理简洁安全——无需显式 null 检查。
var name: String? = "Alice"
println(name?.length) // 5
name = null
println(name?.length) // null
println(name?.length ?: 0) // 0 (elvis: default if null)
println(name?.length ?: return) // early return
// Safe call chain
val city: String? = user?.address?.citylet(空检查)
let 仅在值非空时执行块。'it' 是块内的非空值。用于 null 守卫操作而非 if-null 检查。常见模式:value?.let { ... } 进行安全处理。返回块的结果。