本文汇总了 Kotlin 中一些实用且常用的惯用语法。如果您有常用的惯用语法,可通过提交拉取请求(pull request)补充。
(注:文档翻译部分由 AI 完成)
1.创建数据传输对象(DTOs,即 POJOs/POCOs)
kotlin
data class Customer(val name: String, val email: String)
上述代码定义的 Customer 类自动具备以下功能:
-
所有属性的 getter 方法(若属性为 var 类型,还会生成 setter 方法)
-
equals() 方法(用于对象相等性判断)
-
hashCode() 方法(用于哈希值计算,支持集合中的快速查找)
-
toString() 方法(返回属性信息的字符串表示,便于调试)
-
copy() 方法(用于复制对象并修改部分属性)
-
component1()、component2()...... 等组件函数(支持解构赋值,见"数据类"相关内容)
补充代码示例
kotlin
// 1. 创建对象
val customer1 = Customer("张三", "zhangsan@example.com")
// 2. 调用 toString()
println(customer1) // 输出:Customer(name=张三, email=zhangsan@example.com)
// 3. 解构赋值(使用 component 函数)
val (name, email) = customer1
println("姓名:$name,邮箱:$email") // 输出:姓名:张三,邮箱:zhangsan@example.com
// 4. 复制对象并修改属性
val customer2 = customer1.copy(email = "zhangsan_new@example.com")
println(customer2) // 输出:Customer(name=张三, email=zhangsan_new@example.com)
重点记忆
data 关键字是核心,自动生成 6 大常用方法,避免重复编码;解构赋值和 copy() 是数据类最常用的便捷特性。
2.函数参数默认值
kotlin
fun foo(a: Int = 0, b: String = "") {
println("a: $a, b: $b")
}
补充代码示例
kotlin
// 1. 不传递任何参数,使用默认值
foo() // 输出:a: 0, b:
// 2. 传递部分参数
foo(10) // 输出:a: 10, b:
// 3. 传递指定参数(可打乱顺序)
foo(b = "hello", a = 20) // 输出:a: 20, b: hello
重点记忆
参数后加"= 默认值"即可设置默认值;调用时可指定参数名传递,灵活适配不同传参场景。
3.过滤列表
kotlin
val positives = list.filter { x -> x > 0 }
// 更简洁的写法:
val positives = list.filter { it > 0 }
补充代码示例
kotlin
val numbers = listOf(-3, -2, 0, 1, 2, 3)
// 过滤正整数
val positives = numbers.filter { it > 0 }
println(positives) // 输出:[1, 2, 3]
// 过滤非负数并转换为字符串
val nonNegativeStr = numbers.filter { it >= 0 }.map { it.toString() }
println(nonNegativeStr) // 输出:[0, 1, 2, 3]
重点记忆
filter 接收 lambda 表达式作为条件;当 lambda 只有一个参数时,可用 it 代替参数名简化代码。
了解 Java 与 Kotlin 过滤列表的区别。
4.检查集合中是否存在某元素
kotlin
if ("john@example.com" in emailsList) {
println("约翰的邮箱存在")
}
if ("jane@example.com" !in emailsList) {
println("简的邮箱不存在")
}
补充代码示例
kotlin
val fruits = listOf("苹果", "香蕉", "橙子")
val target = "香蕉"
if (target in fruits) {
println("集合中包含 $target") // 输出:集合中包含 香蕉
} else {
println("集合中不包含 $target")
}
// 结合 when 使用
val fruit = "葡萄"
when (fruit) {
in fruits -> println("$fruit 是常见水果")
else -> println("$fruit 不是常见水果") // 输出:葡萄 不是常见水果
}
重点记忆
用 in 表示"存在于",!in 表示"不存在于",语法直观,替代 Java 中的 contains() 方法。
5.字符串插值
kotlin
println("Name $name")
补充代码示例
kotlin
val name = "李四"
val age = 30
// 直接插入变量
println("姓名:$name,年龄:$age") // 输出:姓名:李四,年龄:30
// 插入表达式
println("明年年龄:${age + 1}") // 输出:明年年龄:31
// 插入函数调用结果
fun getGender() = "男"
println("性别:${getGender()}") // 输出:性别:男
// 插入对象属性
val customer = Customer("王五", "wangwu@example.com")
println("客户邮箱:${customer.email}") // 输出:客户邮箱:wangwu@example.com
重点记忆
用 <math xmlns="http://www.w3.org/1998/Math/MathML"> 符号引用变量,复杂表达式用 符号引用变量,复杂表达式用 </math>符号引用变量,复杂表达式用{} 包裹;替代 Java 中繁琐的 + 拼接或 String.format()。
了解 Java 与 Kotlin 字符串拼接的区别。
6.安全读取标准输入
kotlin
// 读取字符串,若无法转换为整数则返回 null(例如输入"你好!")
val wrongInt = readln().toIntOrNull()
println(wrongInt)
// 输出:null
// 读取可转换为整数的字符串并返回整数(例如输入"13")
val correctInt = readln().toIntOrNull()
println(correctInt)
// 输出:13
补充代码示例
kotlin
println("请输入一个整数:")
val input = readln().toIntOrNull()
// 结合空安全处理
val number = input ?: run {
println("输入无效,使用默认值 0")
0
}
println("最终数值:$number")
// 测试1:输入 25 → 输出:最终数值:25
// 测试2:输入 abc → 输出:输入无效,使用默认值 0;最终数值:0
重点记忆
使用 toIntOrNull() 等 *OrNull() 系列方法,避免转换失败抛出异常,返回 null 更安全。
更多信息请参考"读取标准输入"相关内容。
7.类型检查
kotlin
when (x) {
is Foo -> ... // x 是 Foo 类型时执行
is Bar -> ... // x 是 Bar 类型时执行
else -> ... // 其他类型时执行
}
补充代码示例
kotlin
open class Animal
class Dog : Animal() {
fun bark() = println("汪汪叫")
}
class Cat : Animal() {
fun meow() = println("喵喵叫")
}
fun animalSound(animal: Animal) {
when (animal) {
is Dog -> animal.bark() // 自动类型转换,可直接调用 Dog 方法
is Cat -> animal.meow() // 自动类型转换
else -> println("未知动物叫声")
}
}
val dog = Dog()
val cat = Cat()
animalSound(dog) // 输出:汪汪叫
animalSound(cat) // 输出:喵喵叫
重点记忆
用 is 进行类型判断,判断后自动进行类型转换(智能转换),无需手动强转。
8.只读列表
kotlin
val list = listOf("a", "b", "c")
补充代码示例
kotlin
// 创建空只读列表
val emptyList = listOf<String>()
println(emptyList.isEmpty()) // 输出:true
// 创建包含多种类型元素的只读列表(不推荐,建议统一类型)
val mixedList = listOf("苹果", 10, true)
println(mixedList[0]) // 输出:苹果
// 只读列表不可修改,以下代码会编译报错
// list.add("d")
// list[0] = "x"
// 若需修改,可转换为可变列表
val mutableList = list.toMutableList()
mutableList.add("d")
println(mutableList) // 输出:[a, b, c, d]
重点记忆
用 listOf() 创建只读列表,不支持 add、remove 等修改操作;如需修改,转换为 mutableListOf() 实现。
9.只读映射
kotlin
val map = mapOf("a" to 1, "b" to 2, "c" to 3)
补充代码示例
kotlin
// 创建空只读映射
val emptyMap = mapOf<String, Int>()
// 创建包含多个键值对的映射
val userMap = mapOf("name" to "赵六", "age" to 25, "gender" to "男")
// 读取值
println("姓名:${userMap["name"]}") // 输出:姓名:赵六
// 读取不存在的键,返回 null
println("address:${userMap["address"]}") // 输出:address:null
// 结合 getOrElse 处理不存在的键
val address = userMap.getOrElse("address") { "未知地址" }
println("地址:$address") // 输出:地址:未知地址
// 只读映射不可修改,以下代码编译报错
// userMap["age"] = 26
// userMap.put("address", "北京")
重点记忆
用 mapOf() 创建只读映射,键值对用"键 to 值"表示;读取不存在的键返回 null,而非抛出异常。
10.访问映射条目
kotlin
println(map["key"])
map["key"] = value // 仅可变映射支持
补充代码示例
kotlin
// 只读映射访问
val readOnlyMap = mapOf("one" to 1, "two" to 2)
println(readOnlyMap["one"]) // 输出:1
// 可变映射访问与修改
val mutableMap = mutableMapOf("one" to 1, "two" to 2)
// 修改已有键的值
mutableMap["one"] = 10
// 添加新键值对
mutableMap["three"] = 3
println(mutableMap) // 输出:{one=10, two=2, three=3}
// 用 getValue 读取,不存在则抛出异常
try {
println(mutableMap.getValue("four"))
} catch (e: NoSuchElementException) {
println("键 four 不存在") // 输出:键 four 不存在
}
重点记忆
只读映射仅支持读取([] 运算符),可变映射(mutableMapOf())支持读写([] 赋值);getValue() 不存在时抛异常。
11.遍历映射或键值对列表
kotlin
for ((k, v) in map) {
println("$k -> $v")
}
k 和 v 可以是任意便捷的名称,例如 name 和 age。
补充代码示例
kotlin
val productMap = mapOf("手机" to 5999, "电脑" to 8999, "平板" to 2999)
// 遍历映射的键值对
for ((product, price) in productMap) {
println("$product 的价格:$price 元")
}
// 输出:
// 手机 的价格:5999 元
// 电脑 的价格:8999 元
// 平板 的价格:2999 元
// 遍历键值对列表
val pairList = listOf("苹果" to "红", "香蕉" to "黄", "橙子" to "橙")
for ((fruit, color) in pairList) {
println("$fruit 是 $color 色的")
}
// 输出:
// 苹果 是 红色的
// 香蕉 是 黄色的
// 橙子 是 橙色的
重点记忆
用 (k, v) 解构键值对进行遍历,变量名可自定义,比 Java 遍历 entrySet() 更简洁。
12.遍历区间
kotlin
for (i in 1..100) { ... } // 闭区间:包含 100
for (i in 1..<100) { ... } // 开区间:不包含 100
for (x in 2..10 step 2) { ... } // 步长为 2
for (x in 10 downTo 1) { ... } // 倒序遍历
(1..10).forEach { ... } // 结合 forEach 遍历
补充代码示例
kotlin
// 1. 闭区间遍历(1 到 5,包含 5)
print("闭区间 1..5:")
for (i in 1..5) {
print("$i ") // 输出:闭区间 1..5:1 2 3 4 5
}
println()
// 2. 开区间遍历(1 到 5,不包含 5)
print("开区间 1..<5:")
for (i in 1..<5) {
print("$i ") // 输出:开区间 1..<5:1 2 3 4
}
println()
// 3. 步长遍历(2 到 10,每次加 2)
print("步长 2 遍历 2..10:")
for (x in 2..10 step 2) {
print("$x ") // 输出:步长 2 遍历 2..10:2 4 6 8 10
}
println()
// 4. 倒序遍历(10 到 1)
print("倒序遍历 10 downTo 1:")
for (x in 10 downTo 1 step 3) {
print("$x ") // 输出:倒序遍历 10 downTo 1:10 7 4 1
}
println()
// 5. 结合 forEach
print("forEach 遍历 1..3:")
(1..3).forEach { print("$it ") } // 输出:forEach 遍历 1..3:1 2 3
println()
重点记忆
.. 表示闭区间,..< 表示开区间;step 设步长,downTo 倒序;区间遍历语法直观,替代 Java 中的 for (int i=...; ...; ...)。
13.延迟初始化属性
kotlin
val p: String by lazy { // 仅在第一次访问时计算值
// 计算字符串的逻辑
"延迟初始化的值"
}
补充代码示例
kotlin
// 模拟耗时计算
fun fetchData(): String {
println("开始获取数据(耗时操作)")
Thread.sleep(1000) // 模拟耗时
return "从服务器获取的用户数据"
}
// 延迟初始化属性
val userData: String by lazy {
fetchData() // 第一次访问时才执行
}
println("程序启动")
// 第一次访问,触发 lazy 中的逻辑
println("第一次获取数据:$userData")
// 输出:
// 程序启动
// 开始获取数据(耗时操作)
// 第一次获取数据:从服务器获取的用户数据
// 第二次访问,直接使用缓存的值
println("第二次获取数据:$userData")
// 输出:第二次获取数据:从服务器获取的用户数据
重点记忆
用 by lazy 修饰 val 属性,首次访问时执行 lambda 计算值并缓存,后续访问直接用缓存值,适合耗时初始化场景。
14.扩展函数
kotlin
fun String.spaceToCamelCase() {
// 将空格分隔的字符串转为驼峰式
return this.split(" ").joinToString("") { it.capitalize() }
}
"Convert this to camelcase".spaceToCamelCase()
补充代码示例
kotlin
// 为 String 扩展反转字符串函数
fun String.reverseString(): String {
return this.reversed()
}
val str = "hello"
println(str.reverseString()) // 输出:olleh
// 为 Int 扩展判断是否为偶数的函数
fun Int.isEven(): Boolean {
return this % 2 == 0
}
val num1 = 4
val num2 = 5
println("$num1 是偶数吗?${num1.isEven()}") // 输出:4 是偶数吗?true
println("$num2 是偶数吗?${num2.isEven()}") // 输出:5 是偶数吗?false
// 为 List<Int> 扩展求和函数
fun List<Int>.sumAll(): Int {
var total = 0
this.forEach { total += it }
return total
}
val numbers = listOf(1, 2, 3, 4)
println("列表求和:${numbers.sumAll()}") // 输出:列表求和:10
重点记忆
语法:fun 目标类型.函数名(参数) { ... };无需继承即可为现有类扩展功能,避免工具类冗余。
15.创建单例
kotlin
object Resource {
val name = "Name"
fun getResourceInfo() = "资源名称:$name"
}
补充代码示例
kotlin
// 单例对象:应用配置
object AppConfig {
val appName = "Kotlin 应用"
val version = "1.0.0"
fun getConfigInfo(): String {
return "应用名称:$appName,版本:$version"
}
}
// 直接通过对象名访问,无需创建实例
println(AppConfig.appName) // 输出:Kotlin 应用
println(AppConfig.getConfigInfo()) // 输出:应用名称:Kotlin 应用,版本:1.0.0
// 单例中可包含静态常量和方法(替代 Java 中的 static)
object MathUtils {
const val PI = 3.1415926
fun circleArea(radius: Double): Double {
return PI * radius * radius
}
}
println("圆面积(半径 2):${MathUtils.circleArea(2.0)}") // 输出:圆面积(半径 2):12.5663704
重点记忆
用 object 关键字定义单例,全局唯一实例,直接通过对象名访问;替代 Java 中的饿汉式单例或枚举单例。
16.使用内联值类实现类型安全
kotlin
@JvmInline
value class EmployeeId(private val id: String)
@JvmInline
value class CustomerId(private val id: String)
若不小心混淆了 EmployeeId 和 CustomerId,会触发编译错误。
仅在 JVM 后端中需要 @JvmInline 注解。
补充代码示例
kotlin
// 内联值类:用户 ID 和订单 ID
@JvmInline
value class UserId(private val id: Long)
@JvmInline
value class OrderId(private val id: Long)
// 函数:根据用户 ID 查询订单
fun queryOrders(userId: UserId) {
println("查询用户 ${userId} 的订单")
}
// 正确使用
val userId = UserId(1001)
queryOrders(userId) // 输出:查询用户 UserId(1001) 的订单
// 错误使用:传递 OrderId 会编译报错
val orderId = OrderId(2001)
// queryOrders(orderId) // 编译错误:Type mismatch: inferred type is OrderId but UserId was expected
// 为内联值类扩展函数
fun UserId.getUserIdStr(): String {
return "U${this.toString().replace("UserId(", "").replace(")", "")}"
}
println(userId.getUserIdStr()) // 输出:U1001
重点记忆
用 value class 定义,包装基本类型;编译时检查类型,避免同类型值混淆(如 ID 类型),且性能接近基本类型。
17.实例化抽象类
kotlin
abstract class MyAbstractClass {
abstract fun doSomething()
abstract fun sleep()
}
fun main() {
val myObject = object : MyAbstractClass() {
override fun doSomething() {
println("执行 doSomething")
}
override fun sleep() {
println("执行 sleep")
}
}
myObject.doSomething() // 输出:执行 doSomething
myObject.sleep() // 输出:执行 sleep
}
补充代码示例
kotlin
// 抽象类:形状
abstract class Shape {
abstract fun calculateArea(): Double
abstract fun calculatePerimeter(): Double
}
fun main() {
// 匿名对象实例化抽象类(创建临时矩形实例)
val rectangle = object : Shape() {
private val length = 4.0
private val width = 5.0
override fun calculateArea(): Double {
return length * width
}
override fun calculatePerimeter(): Double {
return 2 * (length + width)
}
}
println("矩形面积:${rectangle.calculateArea()}") // 输出:矩形面积:20.0
println("矩形周长:${rectangle.calculatePerimeter()}") // 输出:矩形周长:18.0
// 作为参数传递
fun printShapeInfo(shape: Shape) {
println("面积:${shape.calculateArea()},周长:${shape.calculatePerimeter()}")
}
printShapeInfo(rectangle) // 输出:面积:20.0,周长:18.0
}
重点记忆
用 object : 抽象类() { 重写抽象方法 } 创建匿名对象实例化抽象类;适合临时使用抽象类实例的场景,无需定义具体子类。
18.非空缩写(If-not-null shorthand)
kotlin
val files = File("Test").listFiles()
println(files?.size) // 若 files 非空则打印大小
补充代码示例
kotlin
data class User(val name: String, val address: Address?)
data class Address(val city: String, val street: String?)
// 创建用户,address 可能为 null
val user1 = User("张三", Address("北京", "中关村大街"))
val user2 = User("李四", null)
// 访问可能为 null 的属性,用 ?. 避免空指针
println("user1 城市:${user1.address?.city}") // 输出:user1 城市:北京
println("user2 城市:${user2.address?.city}") // 输出:user2 城市:null
// 链式调用
println("user1 街道:${user1.address?.street}") // 输出:user1 街道:中关村大街
// 若 address 为 null,后续调用不执行,直接返回 null
println("user2 街道:${user2.address?.street}") // 输出:user2 街道:null
重点记忆
用 ?. 访问可能为 null 的属性或方法,若对象为 null 则整个表达式返回 null,避免空指针异常(NPE)。
19.非空否则缩写(If-not-null-else shorthand)
kotlin
val files = File("Test").listFiles()
// 简单默认值场景:
println(files?.size ?: "empty") // 若 files 为 null,打印 "empty"
// 复杂默认值场景(用 run 代码块):
val filesSize = files?.size ?: run {
val someSize = getSomeSize()
someSize * 2
}
println(filesSize)
补充代码示例
kotlin
// 简单默认值
val name: String? = null
val displayName = name ?: "匿名用户"
println(displayName) // 输出:匿名用户
// 结合函数调用
fun getDefaultAge() = 18
val age: Int? = null
val displayAge = age ?: getDefaultAge()
println(displayAge) // 输出:18
// 复杂默认值(run 代码块)
fun calculateDefaultScore(): Int {
println("计算默认分数(复杂逻辑)")
return 60
}
val score: Int? = null
val displayScore = score ?: run {
println("分数为 null,使用默认计算逻辑")
calculateDefaultScore()
}
println("最终分数:$displayScore")
// 输出:
// 分数为 null,使用默认计算逻辑
// 计算默认分数(复杂逻辑)
// 最终分数:60
重点记忆
?: 运算符表示"若左侧非空则用左侧,否则用右侧";复杂默认值用 run 代码块包裹,延迟执行。
21.为 null 时执行表达式(Execute an expression if null)
kotlin
val values = mapOf("name" to "张三")
val email = values["email"] ?: throw IllegalStateException("Email is missing!")
补充代码示例
kotlin
// 1. 基础用法:必要值缺失抛异常
val values = mapOf("name" to "张三")
// 邮箱为必要参数,缺失则抛异常
val email = values["email"] ?: throw IllegalStateException("Email is missing!")
// 2. 官方推荐:结合run执行多语句
val userName = values["username"] ?: run {
println("用户名缺失,使用默认用户名")
"default_user" // 多语句场景返回默认值
}
println("当前用户:$userName") // 输出:用户名缺失,使用默认用户名;当前用户:default_user
// 3. 结合对象属性使用
data class User(val name: String?, val phone: String?)
val user = User("李四", null)
// 手机号缺失时执行提示并返回默认值
val userPhone = user.phone ?: run {
println("用户${user.name}未绑定手机号")
"未知号码"
}
println(userPhone) // 输出:用户李四未绑定手机号;未知号码
// 4. 用TODO()标记未完善的异常处理逻辑
fun getRequiredParam(params: Map<String, String>, key: String): String {
return params[key] ?: run {
// TODO("待完善:记录缺失参数的日志,便于问题排查")
throw IllegalArgumentException("参数 $key 不能为空")
}
}
重点记忆
- 核心用法:结合 ?: 与 throw 实现"必要值缺失快速抛异常",替代冗长的 if (null) throw 逻辑;
- 多语句场景:用 ?: run { ... } 包裹复杂逻辑,代码块最后一行作为返回值;
- 未完成标记:用 TODO() 函数标记待完善逻辑,IDE会高亮提示未完成。
结合 ?: 与 throw 实现"必要值缺失时快速抛异常",替代冗长的 if (null) throw 逻辑,代码更简洁。
kotlin
// 从映射中获取必要参数,不存在则抛异常
val userParams = mapOf("username" to "zhangsan", "password" to "123456")
// 必要参数:username 和 password
val username = userParams["username"] ?: throw IllegalArgumentException("用户名不能为空")
val password = userParams["password"] ?: throw IllegalArgumentException("密码不能为空")
println("用户名:$username,密码:$password") // 输出:用户名:zhangsan,密码:123456
// 从对象中获取必要属性
data class Product(val id: String?, val name: String?)
val product = Product(null, "手机")
val productId = product.id ?: throw IllegalStateException("产品 ID 不能为空")
// 执行到此处会抛异常:IllegalStateException: 产品 ID 不能为空
kotlin
// 从映射中获取必要参数,不存在则抛异常
val userParams = mapOf("username" to "zhangsan", "password" to "123456")
// 必要参数:username 和 password
val username = userParams["username"] ?: throw IllegalArgumentException("用户名不能为空")
val password = userParams["password"] ?: throw IllegalArgumentException("密码不能为空")
println("用户名:$username,密码:$password") // 输出:用户名:zhangsan,密码:123456
// 从对象中获取必要属性
data class Product(val id: String?, val name: String?)
val product = Product(null, "手机")
val productId = product.id ?: throw IllegalStateException("产品 ID 不能为空")
// 执行到此处会抛异常:IllegalStateException: 产品 ID 不能为空
22.获取可能为空的集合的第一个元素
kotlin
val emails = ... // 可能为空的集合
val mainEmail = emails.firstOrNull() ?: ""
了解 Java 与 Kotlin 获取第一个元素的区别。
补充代码示例
kotlin
// 1. 非空集合获取第一个元素
val fruits = listOf("苹果", "香蕉", "橙子")
val firstFruit = fruits.firstOrNull() ?: "无水果"
println(firstFruit) // 输出:苹果
// 2. 空集合获取第一个元素(返回默认值)
val emptyFruits = emptyList<String>()
val firstEmptyFruit = emptyFruits.firstOrNull() ?: "无水果"
println(firstEmptyFruit) // 输出:无水果
// 3. 带条件获取第一个元素,无匹配则返回默认值
val numbers = listOf(1, 2, 3, 4, 5)
// 获取第一个大于 3 的元素,无则返回 0
val firstGreaterThan3 = numbers.firstOrNull { it > 3 } ?: 0
println(firstGreaterThan3) // 输出:4
// 4. 对比 first() 方法(无匹配时抛异常)
try {
val firstLessThan0 = numbers.first { it < 0 }
} catch (e: NoSuchElementException) {
println("无小于 0 的元素") // 输出:无小于 0 的元素
}
重点记忆
firstOrNull() 为空或无匹配元素时返回 null,结合 ?: 设默认值;first() 无匹配时抛异常,需谨慎使用。
23.非空时执行代码(Execute if not null)
kotlin
val value = ...
value?.let {
... // 非空时执行此代码块
}
补充代码示例
kotlin
// 1. 非空时执行打印
val message: String? = "Hello Kotlin"
message?.let {
println("消息内容:$it") // 输出:消息内容:Hello Kotlin
}
// 2. 空时不执行
val nullMessage: String? = null
nullMessage?.let {
println("空消息不会执行此代码") // 无输出
}
// 3. 结合链式调用处理非空数据
data class User(val name: String?, val age: Int?)
val user = User("张三", 30)
user?.name?.let { userName ->
user.age?.let { userAge ->
println("用户名:$userName,年龄:$userAge") // 输出:用户名:张三,年龄:30
}
}
// 4. 非空时作为参数传递
fun printLength(str: String) {
println("字符串长度:${str.length}")
}
val testStr: String? = "测试字符串"
testStr?.let { printLength(it) } // 输出:字符串长度:5
重点记忆
value?.let { ... } 表示"若 value 非空,则将其作为 it 传入代码块执行";避免嵌套 if (value != null) 逻辑。
24.非空时映射可空值(Map nullable value if not null)
kotlin
val value = ...
val mapped = value?.let { transformValue(it) } ?: defaultValue
// 若 value 为 null 或转换结果为 null,返回 defaultValue
补充代码示例
kotlin
// 1. 非空时转换,空时用默认值
fun transformNumber(num: Int): Int {
return num * 2
}
val num1: Int? = 5
val mapped1 = num1?.let { transformNumber(it) } ?: 0
println(mapped1) // 输出:10(5*2)
val num2: Int? = null
val mapped2 = num2?.let { transformNumber(it) } ?: 0
println(mapped2) // 输出:0(默认值)
// 2. 转换结果可能为 null 时的处理
fun parseStringToInt(str: String): Int? {
return str.toIntOrNull()
}
val str1: String? = "10"
val result1 = str1?.let { parseStringToInt(it) } ?: -1
println(result1) // 输出:10(转换成功)
val str2: String? = "abc"
val result2 = str2?.let { parseStringToInt(it) } ?: -1
println(result2) // 输出:-1(转换结果为 null,用默认值)
val str3: String? = null
val result3 = str3?.let { parseStringToInt(it) } ?: -1
println(result3) // 输出:-1(原值为 null,用默认值)
重点记忆
value?.let { 转换逻辑 } ?: 默认值,覆盖"原值空"和"转换结果空"两种场景,实现安全转换+默认值兜底。
25.when 语句返回值(Return on when statement)
kotlin
fun transform(color: String): Int {
return when (color) {
"Red" -> 0
"Green" -> 1
"Blue" -> 2
else -> throw IllegalArgumentException("Invalid color param value")
}
}
补充代码示例
kotlin
// 1. 基础类型映射
fun getColorCode(color: String): Int {
return when (color) {
"红色" -> 0xFF0000
"绿色" -> 0x00FF00
"蓝色" -> 0x0000FF
else -> throw IllegalArgumentException("不支持的颜色:$color")
}
}
println(getColorCode("红色")) // 输出:16711680
// println(getColorCode("黄色")) // 抛异常:IllegalArgumentException: 不支持的颜色:黄色
// 2. 结合类型判断返回不同类型(需指定返回类型)
fun getTypeInfo(value: Any): String {
return when (value) {
is String -> "字符串类型,长度:${value.length}"
is Int -> "整数类型,值:$value"
is Boolean -> "布尔类型,值:$value"
else -> "未知类型"
}
}
println(getTypeInfo("test")) // 输出:字符串类型,长度:4
println(getTypeInfo(100)) // 输出:整数类型,值:100
println(getTypeInfo(true)) // 输出:布尔类型,值:true
// 3. 简化为单表达式函数
fun getWeekday(index: Int): String = when (index) {
1 -> "周一"
2 -> "周二"
3 -> "周三"
4 -> "周四"
5 -> "周五"
6 -> "周六"
7 -> "周日"
else -> "无效索引"
}
println(getWeekday(3)) // 输出:周三
重点记忆
when 可作为表达式返回值,需覆盖所有可能场景(else 分支);结合单表达式函数语法可大幅简化代码。
26.try-catch 表达式(try-catch expression)
kotlin
fun test() {
val result = try {
count()
} catch (e: ArithmeticException) {
throw IllegalStateException(e)
}
// 处理 result
}
补充代码示例
kotlin
// 1. 捕获异常并返回默认值
fun divide(a: Int, b: Int): Int {
return try {
a / b
} catch (e: ArithmeticException) {
println("除法异常:${e.message}")
0 // 异常时返回默认值 0
}
}
println(divide(10, 2)) // 输出:5
println(divide(10, 0)) // 输出:除法异常:/ by zero;0
// 2. 捕获异常并转换异常类型
fun parseInt(str: String): Int {
return try {
str.toInt()
} catch (e: NumberFormatException) {
throw IllegalArgumentException("无效的整数字符串:$str", e)
}
}
try {
parseInt("abc")
} catch (e: IllegalArgumentException) {
println(e.message) // 输出:无效的整数字符串:abc
}
// 3. 结合 finally 块(finally 不影响返回值)
fun testFinally(): String {
return try {
println("执行 try 块")
return "try 返回值"
} catch (e: Exception) {
"catch 返回值"
} finally {
println("执行 finally 块") // 无论是否异常都会执行
}
}
println(testFinally())
// 输出:
// 执行 try 块
// 执行 finally 块
// try 返回值
重点记忆
try-catch 可作为表达式返回值,异常时执行 catch 分支;finally 块仅用于资源清理,不影响返回值。
17.if 表达式(if expression)
kotlin
val y = if (x == 1) {
"one"
} else if (x == 2) {
"two"
} else {
"other"
}
补充代码示例
kotlin
// 1. 基础条件赋值
val x = 2
val y = if (x == 1) {
"one"
} else if (x == 2) {
"two"
} else {
"other"
}
println(y) // 输出:two
// 2. 简化单行写法
val age = 18
val isAdult = if (age >= 18) true else false
println(isAdult) // 输出:true
// 3. 代码块中执行复杂逻辑后返回值
val score = 85
val grade = if (score >= 90) {
println("成绩优秀")
"A"
} else if (score >= 80) {
println("成绩良好")
"B"
} else if (score >= 60) {
println("成绩及格")
"C"
} else {
println("成绩不及格")
"D"
}
println("等级:$grade")
// 输出:
// 成绩良好
// 等级:B
// 4. 替代三元运算符
val a = 10
val b = 20
val max = if (a > b) a else b
println("最大值:$max") // 输出:最大值:20
重点记忆
Kotlin 无三元运算符,用 if-else 表达式替代;代码块中最后一行表达式即为返回值,无需 return。
18.返回 Unit 的方法的构建器风格用法(Builder-style usage of methods that return Unit)
kotlin
fun arrayOfMinusOnes(size: Int): IntArray {
return IntArray(size).apply { fill(-1) }
}
补充代码示例
kotlin
// 1. 数组初始化并填充值
fun arrayOfZeros(size: Int): IntArray {
// 创建数组后立即调用 fill 方法填充 0
return IntArray(size).apply { fill(0) }
}
val zeroArray = arrayOfZeros(5)
println(zeroArray.contentToString()) // 输出:[0, 0, 0, 0, 0]
// 2. 集合初始化并添加元素
fun createFruitList(): List<String> {
return mutableListOf<String>().apply {
add("苹果")
add("香蕉")
add("橙子")
sort() // 排序
}
}
val fruitList = createFruitList()
println(fruitList) // 输出:[苹果, 橙子, 香蕉](中文按拼音排序)
// 3. 对象初始化并调用多个无返回值方法
class Printer {
fun setColor(color: String) {
println("设置颜色为:$color")
}
fun setSize(size: Int) {
println("设置大小为:$size")
}
fun printContent(content: String) {
println("打印内容:$content")
}
}
fun createPrinter(): Printer {
return Printer().apply {
setColor("黑色")
setSize(10)
// 可链式调用多个无返回值方法
}
}
val printer = createPrinter()
printer.printContent("Kotlin 教程")
// 输出:
// 设置颜色为:黑色
// 设置大小为:10
// 打印内容:Kotlin 教程
重点记忆
用 apply 方法实现构建器风格:创建对象后立即调用多个无返回值方法,最后返回对象本身,代码更连贯。
19.单表达式函数(Single-expression functions)
kotlin
fun theAnswer() = 42
等价于
kotlin
fun theAnswer(): Int {
return 42
}
单表达式函数可与其他惯用语法有效结合,使代码更简洁。例如结合 when 表达式:
kotlin
fun transform(color: String): Int = when (color) {
"Red" -> 0
"Green" -> 1
"Blue" -> 2
else -> throw IllegalArgumentException("Invalid color param value")
}
补充代码示例
kotlin
// 1. 简单返回值函数
fun add(a: Int, b: Int) = a + b
println(add(3, 5)) // 输出:8
// 2. 结合条件表达式
fun isPositive(num: Int) = num > 0
println(isPositive(10)) // 输出:true
println(isPositive(-5)) // 输出:false
// 3. 结合函数调用
fun getGreeting(name: String) = "Hello, $name!"
println(getGreeting("张三")) // 输出:Hello, 张三!
// 4. 结合 when 表达式处理多分支
fun getSeason(month: Int) = when (month) {
3, 4, 5 -> "春季"
6, 7, 8 -> "夏季"
9, 10, 11 -> "秋季"
12, 1, 2 -> "冬季"
else -> "无效月份"
}
println(getSeason(4)) // 输出:春季
println(getSeason(10)) // 输出:秋季
// 5. 带返回值类型注解(复杂场景建议显式指定)
fun multiply(a: Double, b: Double): Double = a * b
println(multiply(2.5, 4.0)) // 输出:10.0
重点记忆
函数体仅一个表达式时用 = 替代大括号和 return;简单场景可省略返回值类型(编译器推导),复杂场景建议显式指定。
20.对对象实例调用多个方法(with)
kotlin
class Turtle {
fun penDown()
fun penUp()
fun turn(degrees: Double)
fun forward(pixels: Double)
}
val myTurtle = Turtle()
with(myTurtle) { // 绘制一个 100 像素的正方形
penDown()
for (i in 1..4) {
forward(100.0)
turn(90.0)
}
penUp()
}
补充代码示例
kotlin
// 1. 对字符串调用多个方法
val str = " Kotlin Tutorial "
val processedStr = with(str) {
trim() // 去除前后空格
.lowercase() // 转为小写
.replace("tutorial", "教程") // 替换内容
}
println(processedStr) // 输出:kotlin 教程
// 2. 对集合调用多个方法
val numbers = listOf(3, 1, 4, 1, 5, 9)
val result = with(numbers) {
sorted() // 排序
.distinct() // 去重
.take(3) // 取前 3 个元素
}
println(result) // 输出:[1, 3, 4]
// 3. 自定义类对象调用多个方法
class Student {
var name = ""
var age = 0
var grade = ""
fun study() = println("$name 正在学习")
fun exam() = println("$name 正在考试")
}
val student = Student()
with(student) {
name = "李四"
age = 18
grade = "高三"
study()
exam()
}
// 输出:
// 李四 正在学习
// 李四 正在考试
// 4. with 返回代码块最后一个表达式的值
val studentInfo = with(student) {
"姓名:$name,年龄:$age,年级:$grade"
}
println(studentInfo) // 输出:姓名:李四,年龄:18,年级:高三
重点记忆
with(对象) { ... } 中可直接调用对象方法/访问属性,无需重复写对象名;代码块最后一个表达式为返回值。
21.配置对象属性(apply)
kotlin
val myRectangle = Rectangle().apply {
length = 4
breadth = 5
color = 0xFAFAFA
}
这在配置对象构造函数中不存在的属性时非常有用。
补充代码示例
kotlin
// 1. 配置数据类属性(无参构造需单独指定)
data class Person(var name: String = "", var age: Int = 0, var address: String = "")
val person = Person().apply {
name = "王五"
age = 25
address = "北京市海淀区"
}
println(person) // 输出:Person(name=王五, age=25, address=北京市海淀区)
// 2. 配置集合属性
val mutableMap = mutableMapOf<String, Int>().apply {
put("苹果", 10)
put("香蕉", 20)
put("橙子", 15)
remove("香蕉") // 移除不需要的键值对
}
println(mutableMap) // 输出:{苹果=10, 橙子=15}
// 3. 配置自定义类属性(构造函数无对应参数)
class Car {
// 构造函数未包含的属性
var brand: String = ""
var model: String = ""
var year: Int = 0
// 构造函数参数
var price: Double = 0.0
}
val myCar = Car(price = 150000.0).apply {
brand = "丰田"
model = "凯美瑞"
year = 2024
}
println("品牌:${myCar.brand},型号:${myCar.model},年份:${myCar.year},价格:${myCar.price}")
// 输出:品牌:丰田,型号:凯美瑞,年份:2024,价格:150000.0
// 4. 链式配置并返回对象
fun createConfiguredUser(): Person {
return Person().apply {
name = "赵六"
age = 30
}.apply {
address = "上海市浦东新区" // 可链式调用多个 apply
}
}
val configuredUser = createConfiguredUser()
println(configuredUser) // 输出:Person(name=赵六, age=30, address=上海市浦东新区)
重点记忆
apply 与 with 类似,但始终返回对象本身,支持链式调用;适合对象创建后集中配置属性,代码更简洁。
22.Java 7 的 try-with-resources
kotlin
val stream = Files.newInputStream(Paths.get("/some/file.txt"))
stream.buffered().reader().use { reader ->
println(reader.readText())
}
补充代码示例
kotlin
// 1. 读取文件内容(自动关闭流,官方链式use风格)
import java.nio.file.Files
import java.nio.file.Paths
fun readFileContent(filePath: String): String {
// 链式调用use,自动关闭所有实现AutoCloseable的资源
return Files.newInputStream(Paths.get(filePath))
.buffered()
.reader()
.use { it.readText() } // 简洁写法:it指代reader
}
// 测试(需确保文件存在,此处仅为示例)
// val content = readFileContent("test.txt")
// println(content)
// 2. 写入文件内容
fun writeFileContent(filePath: String, content: String) {
Files.newBufferedWriter(Paths.get(filePath)).use { writer ->
writer.write(content)
writer.newLine()
writer.write("这是第二行内容")
}
}
// 测试
// writeFileContent("output.txt", "这是第一行内容")
// 3. 处理数据库连接(多层链式use,自动关闭资源)
import java.sql.Connection
import java.sql.DriverManager
import java.sql.ResultSet
fun queryUserCount(): Int {
val url = "jdbc:mysql://localhost:3306/test"
val user = "root"
val password = "123456"
// 连接、语句、结果集链式use,均会自动关闭
Connection::class.java.forName("com.mysql.cj.jdbc.Driver")
return DriverManager.getConnection(url, user, password)
.createStatement()
.executeQuery("SELECT COUNT(*) FROM user")
.use { it.next(); it.getInt(1) }
}
// 4. 用TODO()标记未完善的资源处理逻辑
fun copyFile(sourcePath: String, targetPath: String) {
Files.newInputStream(Paths.get(sourcePath)).use { input ->
Files.newOutputStream(Paths.get(targetPath)).use { output ->
// TODO("待完善:添加文件复制进度提示功能")
input.transferTo(output)
}
}
}
// 测试
// println("用户数量:${queryUserCount()}")
// copyFile("source.txt", "target.txt")
重点记忆
Kotlin 用 use 函数替代 Java 的 try-with-resources;实现 AutoCloseable 接口的资源调用 use 后会自动关闭,无需手动处理。
23.需要泛型类型信息的泛型函数
kotlin
// public final class Gson {
// ...
// public <T> T fromJson(JsonElement json, Class<T> classOfT) throws JsonSyntaxException {
// ...
inline fun <reified T: Any> Gson.fromJson(json: JsonElement): T = this.fromJson(json, T::class.java)
补充代码示例
kotlin
// 1. 简化 Gson 反序列化(无需传递 Class 对象)
import com.google.gson.Gson
import com.google.gson.JsonParser
// 定义扩展函数
inline fun <reified T : Any> Gson.fromJson(jsonStr: String): T {
val jsonElement = JsonParser.parseString(jsonStr)
return this.fromJson(jsonElement, T::class.java)
}
// 测试
data class User(val name: String, val age: Int)
val gson = Gson()
val jsonStr = "{\"name\":\"张三\",\"age\":30}"
// 无需传递 User::class.java,编译器通过 reified 推导
val user = gson.fromJson<User>(jsonStr)
println(user) // 输出:User(name=张三, age=30)
// 2. 自定义泛型函数(需要类型信息)
inline fun <reified T> isInstanceOf(value: Any): Boolean {
// reified 允许在函数内部获取 T 的类型信息
return value is T
}
println(isInstanceOf<String>("test")) // 输出:true
println(isInstanceOf<Int>("test")) // 输出:false
// 3. 泛型集合转换(需要元素类型信息)
inline fun <reified T> List<*>.filterIsInstanceToList(): List<T> {
val result = mutableListOf<T>()
for (element in this) {
if (element is T) {
result.add(element)
}
}
return result
}
val mixedList = listOf("a", 1, 2.0, "b", 3)
val stringList = mixedList.filterIsInstanceToList<String>()
val intList = mixedList.filterIsInstanceToList<Int>()
println(stringList) // 输出:[a, b]
println(intList) // 输出:[1, 2, 3]
重点记忆
用 inline + reified 修饰泛型参数,可在函数内部获取泛型类型信息(T::class.java);解决泛型擦除导致无法获取类型的问题。
24.交换两个变量(Swap two variables)
kotlin
var a = 1
var b = 2
a = b.also { b = a }
补充代码示例
kotlin
// 1. 基础类型变量交换(核心实现)
var a = 10
var b = 20
println("交换前:a=$a, b=$b") // 输出:交换前:a=10, b=20
// 核心语法:also函数先执行括号内赋值,再返回原始值
a = b.also { b = a }
println("交换后:a=$a, b=$b") // 输出:交换后:a=20, b=10
// 2. 引用类型变量交换(字符串、对象等)
var str1 = "Kotlin"
var str2 = "Java"
println("引用类型交换前:str1=$str1, str2=$str2") // 输出:str1=Kotlin, str2=Java
str1 = str2.also { str2 = str1 }
println("引用类型交换后:str1=$str1, str2=$str2") // 输出:str1=Java, str2=Kotlin
// 3. 集合中指定索引元素交换
val mutableList = mutableListOf(10, 20, 30, 40)
println("集合交换前:$mutableList") // 输出:[10, 20, 30, 40]
// 交换索引1和3位置的元素
mutableList[1] = mutableList[3].also { mutableList[3] = mutableList[1] }
println("集合交换后:$mutableList") // 输出:[10, 40, 30, 20]
// 4. 对比传统临时变量方式(效果一致,代码略繁琐)
var x = 5
var y = 8
println("传统方式交换前:x=$x, y=$y") // 输出:x=5, y=8
val temp = x
x = y
y = temp
println("传统方式交换后:x=$x, y=$y") // 输出:x=8, y=5
// 5. 错误示范:不可变变量(val)无法交换
// val c = 3
// val d = 4
// c = d.also { d = c } // 编译报错:Val cannot be reassigned
// 6. 用TODO()标记未完成的交换逻辑
fun swapNullable(a: Int?, b: Int?): Pair<Int?, Int?> {
// TODO("待实现:处理a或b为null时的交换逻辑,避免空指针")
return b to a
}
重点记忆
- 核心语法:a = b.also { b = a },利用also函数"先执行副作用(赋值)再返回接收者"的特性实现无临时变量交换;
- 适用范围:支持所有可重新赋值的变量(var修饰),包括基础类型和引用类型;
- 注意事项:不可用于val修饰的不可变变量,因无法重新赋值;
- 标记未完成代码:使用TODO()函数标记待完善逻辑,编译时会提示未完成。
25.标记代码为未完成(Mark code as incomplete (TODO))
Kotlin 标准库中有一个TODO()函数总是会抛出异常NotImplementedError。它的返回类型为 None,Nothing因此无论预期类型如何都可以使用。此外,还有一个重载版本,接受一个 reason 参数:
Kotlin
fun calcTaxes(): BigDecimal = TODO("Waiting for feedback from accounting")
IntelliJ IDEA 的 kotlin 插件能够理解 TODO 工具窗口中的代码指针的语义TODO()并自动添加代码指针。
补充代码示例
kotlin
// 1. 基础用法:无参数TODO(),编译通过但运行抛异常
fun calculateTotal(prices: List<Double>): Double {
TODO() // IDE会高亮提示"未实现的函数"
}
// 2. 带提示信息的TODO(),说明待实现内容
fun formatDate(date: Date): String {
TODO("待实现:按yyyy-MM-dd格式化,处理null和非法日期")
}
// 3. 在代码块中使用,标记分支未完善
fun processOrder(order: Order) {
when (order.status) {
OrderStatus.PAID -> shipOrder(order)
OrderStatus.CANCELLED -> refundOrder(order)
OrderStatus.PENDING -> TODO("待实现:待支付订单的超时提醒逻辑")
}
}
重点记忆
kotlin
// 1. 结合空安全使用TODO()
data class User(val id: Long, val name: String?)
fun getUserById(id: Long): User? {
val user = userRepository.findById(id)
return user ?: run {
TODO("待实现:用户不存在时的日志记录和默认用户返回")
null
}
}
// 2. 接口实现中标记未完成方法
interface PaymentService {
fun pay(amount: Double)
fun refund(amount: Double)
}
class AlipayService : PaymentService {
override fun pay(amount: Double) {
// 已实现支付逻辑
println("支付宝支付 $amount 元")
}
override fun refund(amount: Double) {
TODO("待对接支付宝退款接口,处理退款失败场景")
}
}
// 3. 测试用例中标记未完成场景
fun testOrderFlow() {
// 已完成:测试支付成功流程
val paidOrder = Order(1, OrderStatus.PAID)
check(shipOrder(paidOrder) == true)
// 未完成:测试支付超时流程
val timeoutOrder = Order(2, OrderStatus.PENDING)
TODO("待测试:超过30分钟未支付的订单自动取消逻辑")
}
// 4. 与其他惯用语法结合(如try-catch)
fun parseData(data: String): Data {
return try {
JsonParser.parseString(data).toData()
} catch (e: JsonSyntaxException) {
TODO("待实现:JSON解析异常的重试机制,当前直接抛异常")
throw e
}
}
- 核心特性:TODO()是Kotlin标准库函数,编译时不报错(支持临时调试),运行时抛出NotImplementedError,IDE会高亮提示未完成;
- 实用技巧:传字符串参数明确待实现细节,可在函数、代码块、接口、测试用例等多场景使用;
- 最佳实践:上线前需通过IDE全局搜索(TODO)清理所有未完成代码,避免线上抛异常;
- 关联用法:可与空安全、try-catch等语法结合,标记局部未完善逻辑。
26.Kotlin 常用惯用语法核心特点总结
Kotlin 惯用语法围绕简洁性、安全性、高效性三大核心,通过语法层面的优化减少样板代码、降低异常风险,同时提升开发效率与代码可读性,核心特点如下:
| 核心维度 | 关键语法 | 核心优势 |
|---|---|---|
| 简洁高效 | data class、单表达式函数、字符串插值、默认参数、扩展函数 | 自动生成常用方法(如 equals、copy),简化变量拼接、函数定义与现有类功能扩展,减少冗余编码 |
| 空安全保障 | ?.、?:、let、toIntOrNull 等 *OrNull () 方法 | 编译时检查空指针风险,通过安全调用、默认值兜底、非空执行等语法,彻底规避空指针异常 |
| 便捷集合操作 | listOf/mapOf、filter/map、区间遍历、解构赋值 | 快速创建只读集合,支持链式过滤转换,遍历语法直观(闭区间 / 开区间 / 倒序),解构赋值简化数据获取 |
| 对象与资源管理 | apply/with、use 函数、object 单例 | 集中配置对象属性、简化多方法调用,自动管理 AutoCloseable 资源(替代 try-with-resources),便捷实现单例 |
| 泛型与类型安全 | inline reified、value class | 解决泛型擦除问题,支持泛型类型信息获取;内联值类实现类型安全,避免同类型值混淆(如 ID 类型) |
| 场景化语法 | when 表达式、firstOrNull、交换变量、延迟初始化 | 覆盖多分支判断、集合元素安全获取、变量交换、耗时初始化等实际场景,兼顾可读性与性能 |
整体来看,Kotlin 惯用语法既贴合实际开发需求,又通过编译时检查(如类型安全、空安全)提升代码可靠性。例如,data class 省去手动编写 getter/setter、equals 等方法的麻烦,空安全语法无需繁琐的空判断,扩展函数避免工具类冗余,use 函数简化资源关闭流程。这些语法设计既减少了开发工作量,又降低了维护成本,同时保持代码简洁易懂,实现了 "写得少、错得少、跑得好" 的开发目标,成为兼顾效率与质量的主流编程语言选择。