1 扩展
扩展函数允许为现有类添加新的方法,而无需修改其源代码。
1.1 扩展函数
// 定义扩展函数
fun String.isUpperCase(): Boolean {
return this == this.uppercase()
}
// 使用扩展函数
fun main() {
println("HELLO".isUpperCase()) // 输出: true
}
1.2 扩展属性
// 定义扩展属性
val String.firstLetter: Char
get() = this[0]
// 使用扩展属性
fun main() {
println("HELLO".firstLetter) // 输出: H
}
2 委托
委托是 Kotlin 中实现“组合优于继承”的重要特性。
2.1 属性委托
属性委托允许将属性的逻辑委托给另一个对象,从而实现更灵活的属性管理。属性委托的核心是通过 getValue 和 setValue 方法控制属性的读取和写入。和重写set get 不同的是它是可以独立作为一个模块,适合一些共用委托逻辑的处理。
应用场景:将通用逻辑(如数据验证、日志记录)抽离出来,实现复用。
2.1.1 自定义委托
// 实现一个正数验证逻辑的委托
class PositiveNumberDelegate {
private var value: Int = 0
operator fun getValue(thisRef: Any?, property: KProperty<*>): Int {
return value
}
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: Int) {
if (value < 0) {
throw IllegalArgumentException("不是正数")
}
this.value = value
}
}
class User {
// age属性委托给PositiveNumberDelegate
var age: Int by PositiveNumberDelegate()
}
fun main() {
val user = User()
user.age = 25 // 成功
println("Age: ${user.age}") // 输出: Age: 25
try {
user.age = -1 // 抛出异常
} catch (e: IllegalArgumentException) {
println(e.message) // 输出: 不是正数
}
}
2.1.2内置委托
lazy
延迟初始化属性,仅在首次访问时初始化,节省资源。
特点:
- 线程安全:支持
SYNCHRONIZED、VOLATILE和NONE三种线程安全模式。 - 适用于
val:可用于可空不可变属性,属性类型可以为空,但是必须是val修饰的不可变变量。
// 延时初始化resource
val resource: Resource by lazy { Resource() }
map
val map = mapOf("name" to "John Doe", "age" to 25)
val name: String by map
val age: Int by map
println("name: ${name}") // 输出: name: John Doe
println("age: ${age}") // 输出: age: 25
Delegates.observable
观察属性的变化,适用于需要在属性值变化时执行附加操作的场景。
class User {
var name: String by Delegates.observable("初始值") { prop, old, new ->
println("${prop.name}从$old变为$new")
}
}
fun main() {
val user = User()
user.name = "Alice" // 输出: name从初始值变为Alice
user.name = "Bob" // 输出: name从Alice变为Bob
}
Delegates.vetoable
在属性值变化前进行拦截,适用于需要验证属性值的场景。
class User {
var age: Int by Delegates.vetoable(0) { prop, old, new ->
new >= 0 // 验证逻辑
}
}
fun main() {
val user = User()
user.age = 25 // 成功
try {
user.age = -1 // 抛出异常
} catch (e: IllegalArgumentException) {
println("Invalid age: ${e.message}")
}
}
2.2 接口委托
将接口的实现委托给另一个对象,从而实现“组合优于继承”的设计原则。这种方式特别适合将通用逻辑抽离出来,实现复用。
interface Logger {
fun log(message: String)
}
class ConsoleLogger : Logger {
override fun log(message: String) {
println("LOG: $message")
}
}
// 通过接口委托,User有了Logger的功能
class User : Logger by ConsoleLogger() {
init {
// 可以直接调用日志打印方法
log("init")
}
}
3 自定义运算符
Kotlin 允许通过 operator 关键字重载运算符。
示例
data class Point(val x: Int, val y: Int) {
operator fun plus(other: Point): Point {
return Point(x + other.x, y + other.y)
}
}
fun main() {
val p1 = Point(1, 2)
val p2 = Point(3, 4)
val result = p1 + p2
println(result) // 输出: Point(x=4, y=6)
}
支持的运算符
- 加法(
+):plus - 减法(
-):minus - 乘法(
*):times - 除法(
/):div - 模运算(
%):mod - 等等…
4 中缀表达式
Kotlin 允许通过 infix 关键字定义中缀函数,使代码更简洁。
4.1 定义和使用
infix fun String.add(str: String): String {
return this + str
}
fun main() {
println("Hello" add "World") // 输出: HelloWorld
}
4.2 特性
- 中缀函数必须是成员函数或扩展函数。
- 参数必须只有一个。
- 中缀函数的调用方式类似于操作符,但实际上是函数调用。
5 内联函数
内联函数通过 inline 关键字声明,将函数体直接插入到调用点,从而避免函数调用的开销。
5.1定义和使用
// 和reified合用可以获取泛型类型
inline fun <reified T> getType(): String {
return T::class.qualifiedName ?: "Unknown"
}
5.2 特性
-
noinline关键字:显式禁止某个参数被内联。inline fun <T> myFunction(a: () -> T, noinline b: () -> T) { println(a()) // a 被内联 println(b()) // b 不被内联 } -
内联函数的限制:
- 内联函数不能包含非内联的高阶函数调用。
- 内联函数不能包含
return到外部函数的语句。 - 内联函数不能被
override。
6 内部类
内部类通过 inner 关键字修饰,可以访问外部类的成员。
6.1 定义和使用
class Outer {
val outerProperty = "Outer Property"
inner class Inner {
fun showOuterProperty() {
println(outerProperty) // 访问外部类的成员
}
}
}
fun main() {
val inner = Outer().Inner()
inner.showOuterProperty() // 输出: Outer Property
}
6.2 匿名内部类
用于实现接口或继承类。
val runnable = object : Runnable {
override fun run() {
println("Running...")
}
}
7 作用域函数
7.1 apply
特点:
- 行为:对对象执行一系列操作,并返回该对象。
- 返回值:原始对象。
- 上下文:使用
this作为上下文参数。
val person = Person("Alice", 25).apply {
name = "Bob"
age = 30
}
println(person) // 输出: Person(name=Bob, age=30)
7.2 run
特点:
- 行为:对对象执行一系列操作,并返回函数体中的最后一行表达式。
- 返回值:函数体中的最后一行表达式。
- 上下文:使用
this作为上下文参数。
val result = Person("Alice", 25).run {
name = "Bob"
age = 30
"Name: $name, Age: $age"
}
println(result) // 输出: Name: Bob, Age: 30
7.3 let
- 行为:对对象执行一系列操作,并返回函数体中的最后一行表达式。
- 返回值:函数体中的最后一行表达式。
- 上下文:使用
it作为上下文参数。
val result = Person("Alice", 25).let {
it.name = "Bob"
it.age = 30
"Name: ${it.name}, Age: ${it.age}"
}
println(result) // 输出: Name: Bob, Age: 30
7.4 with
- 行为:对对象执行一系列操作,并返回函数体中的最后一行表达式。
- 返回值:函数体中的最后一行表达式。
- 上下文:使用
this作为上下文参数。
val result = with(Person("Alice", 25)) {
name = "Bob"
age = 30
"Name: $name, Age: $age"
}
println(result) // 输出: Name: Bob, Age: 30
8 单例
Kotlin 提供了简洁的单例实现方式。
8.1 定义单例
object Singleton {
fun doSomething() {
println("Singleton instance")
}
}
fun main() {
Singleton.doSomething() // 输出: Singleton instance
}
8.2 特性
- 单例实例在首次使用时被初始化。
- 线程安全。
9 get、set
示例
var name: String = ""
get() = field
set(value) {
if (value.isNotEmpty()) {
field = value
} else {
throw Exception("Name cannot be empty")
}
}
// 如果你想私有set方法可以这样写
var name: String = ""
get
private set
10 空安全
Kotlin 的空安全机制旨在编译期排查可能出现的空指针异常问题,从而提高代码的健壮性。
10.1 变量可空性
通过在类型后添加 ?,可以声明可空类型变量。
var nullableName: String? = "Tom" // 可空类型
nullableName = null // 允许赋值为 null
10.2 空安全调用操作符(?.)
允许在可空对象上调用方法或访问属性,如果对象为 null,则直接返回 null。
val name: String? = null
val length = name?.length // 返回 null,不会抛出异常
10.3 空合并操作符(?:)
?: 用于提供默认值,如果左侧表达式为非空,则返回左侧值,否则返回右侧值。
val name: String? = null
val length = name?.length ?: -1 // 如果 name 为 null,则返回 -1
10.4 非空断言操作符(!!)
!! 将可空类型强制转换为非空类型,如果对象为 null,则抛出 NullPointerException。
val name: String? = null
val length = name!!.length // 抛出 NullPointerException
10.5 延时初始化lateinit
lateinit 用于延迟初始化非空属性,适用于无法在声明时初始化的场景。
lateinit 只能用于非空的可变属性(var),不能用于 val或可空类型。注意和by lazy的区别,by lazy是可空不可变属性,且线程安全。
class MyClass {
lateinit var name: String
fun init(context: Context) {
name = ""
}
}
511

被折叠的 条评论
为什么被折叠?



