完成 深入 Kotlin

This commit is contained in:
SOR
2026-09-21 21:28:49 +08:00
parent 8f289ee0c1
commit 77c99fbc27
+396 -4
View File
@@ -173,46 +173,438 @@ class Square() : Rectangle(), Polygon {
#### 接口
接口
接口里面可以有:
- 抽象函数(没有函数体)
- 带默认实现的函数
- 抽象属性
- 一个写明类型的变量,但不能有默认值
```kotlin
interface Clickable {
fun click()
fun showOff() = println("I'm clickable")
val name: String
}
```
#### 抽象类
抽象类介于接口和类之间,
抽象类介于接口和普通类之间:
- 可以有构造参数、状态、成员变量
- 可以有抽象成员,也可以有具体实现,如果成员变量有默认值,那它就不是`抽象的`
- 抽象成员默认是 `open` 的,子类必须 `override`
- 一个类只能继承一个抽象类,但可以实现多个接口
```kotlin
abstract class Animal {
abstract val name: String
abstract fun sound()
open fun info() = "$name says ${sound()}"
}
class Dog(override val name: String) : Animal() {
override fun sound() = "Woof"
}
```
### 泛型类
泛型类把类型作为参数:
```kotlin
class Box<T>(var value: T)
val stringBox = Box("hello")
val intBox = Box(123)
```
#### 类型约束
```kotlin
class NumberBox<T : Number>(val value: T)
class NumberBox<T>(val value: T) where T : Serializable, T : Number
```
使用 `where` 给 `T` 更多限定,这里,`T` 既是 `Serializable` 的子类,也是 `Number` 的子类。
#### 型变
```kotlin
// out:协变,只能返回 T 类型变量
class Producer<out T>(private val value: T) {
fun get(): T = value
}
// in:逆变,只能接收 T 类型变量
class Consumer<in T> {
fun consume(value: T) {}
}
```
#### 星投影
具体的泛型类型,依据传进来的 list 参数,它允许任意的唯一类型。与 Any 不同的是,使用 Any 指 list 本身的泛型就是 Any。
```kotlin
fun printAll(list: List<*>) {
list.forEach { println(it) }
}
```
### 特殊类
#### object
它常用于单例、匿名对象、伴生对象。
##### 单例
单例可以直接用类名当成对象变量名使用,而且每次使用时调用的对象都是同一个。
```kotlin
object Config {
const val STR = "const" // object 里面可以定义 const 变量
var debug = false
fun log(msg: String) {
if (debug) println(msg)
}
}
```
##### 匿名对象
它用于实现一个接口,或是别的什么东西。
```kotlin
val runnable = object : Runnable {
override fun run() {
println("running")
}
}
```
##### 伴生对象
```kotlin
class User private constructor(val name: String) {
companion object {
const val TABLE = "users"
fun create(name: String) = User(name)
} // 用一个工厂方法代替构造函数,这很常用,它可以做一些更复杂的事。
}
val u = User.create("Tom")
println(User.TABLE)
```
#### data class
数据类自动生成:
- `equals()`
- `hashCode()`
- `toString()`
- `copy()`
- `componentN()`
```kotlin
data class User(val name: String, var age: Int)
val u = User("Tom", 18)
val u2 = u.copy(age = 19)
val (name, age) = u
```
- 主构造至少有一个参数
- 主构造参数必须定义一个成员变量
- 不能是 `abstract`、`open`、`sealed`、`inner`
#### enum class
枚举类表示一组有限常量。
```kotlin
enum class Direction(
val degree: Int // 它指的是枚举变量的属性
) {
NORTH(0),
EAST(90),
SOUTH(180),
WEST(270);
// entries 是 Direction 的成员变量,是所有枚举变量组成的列表
fun turnRight(): Direction = entries[(ordinal + 1) % entries.size]
}
val d = Direction.NORTH
println(d.degree)
```
枚举可以实现接口,但不能继承普通类。每个枚举常量本身是对象。
### 特殊类函数
#### 重写 toString
```kotlin
class Point(val x: Int, val y: Int) {
override fun toString(): String = "Point($x, $y)"
}
println(Point(1, 2)) // Point(1, 2)
```
`data class` 会自动生成 `toString()`。
#### 重载运算符
用 `operator` 修饰函数,让对象支持运算符。常见映射:
| 运算符 | 函数 |
| :-------: | :-----------: |
| + | plus |
| - | minus |
| * | times |
| / | div |
| % | rem |
| += 等 | plusAssign 等 |
| [] | get / set |
| () | invoke |
| in | contains |
| < > <= >= | compareTo |
| .. | rangeTo |
| == | equals |
`compareTo` 函数较为特殊,它返回 `Int`,意思是这样的:a < b 等价于 a.compareTo(b) < 0。这与 CPP / Java 是一样的。
更多函数名请查询 [运算符重载](https://book.kotlincn.net/text/operator-overloading.html)
```kotlin
data class Vec(val x: Int, val y: Int) {
operator fun plus(other: Vec) = Vec(x + other.x, y + other.y)
operator fun get(index: Int) = if (index == 0) x else y
}
val v = Vec(1, 2) + Vec(3, 4)
println(v[0])
```
#### component 函数
#### invoke
`componentN()` 用于解构声明。
类似于 Python 的 `__call__()`
```kotlin
class User(val name: String, val age: Int) {
operator fun component1() = name
operator fun component2() = age
}
val (name, age) = User("Tom", 18)
```
`data class` 会自动生成 `component1()`、`component2()` 等。
#### invoke 函数
`invoke` 让实例可以像函数一样调用,类似 Python 的 `__call__()`。
```kotlin
class Adder(val base: Int) {
operator fun invoke(x: Int): Int = base + x
}
val add10 = Adder(10)
println(add10(5)) // 15
```
## 特殊函数
### 嵌套 lambda 与标签
标签语法:`label@`。
```kotlin
outer@ for (i in 1..3) {
for (j in 1..3) {
if (j == 2) continue@outer
if (i == 3) break@outer
}
}
```
lambda 中常用 `return@label`:
```kotlin
listOf(1, 2, 3).forEach lit@{
if (it == 2) return@lit
println(it)
}
```
如果 lambda 是 `inline` 的,可以直接非局部返回:
```kotlin
fun foo() {
listOf(1, 2, 3).forEach {
if (it == 2) return // 从 foo 返回
}
}
```
嵌套 lambda 中同名隐式标签可能冲突,建议显式命名标签。
### 泛型函数
```kotlin
fun <T> singletonList(item: T): List<T> = listOf(item)
fun <T : Comparable<T>> max(a: T, b: T): T = if (a > b) a else b
```
多个约束用 `where`:
```kotlin
fun <T> ensureTrailingPeriod(seq: T)
where T : CharSequence, T : Appendable {
// 在这个函数内,T 需要同时为 CharSequence 和 Appendable 的子类。
// 这样,seq 就能调用两个父类的函数。
if (!seq.endsWith('.')) seq.append('.')
}
```
### infix 函数
`infix` 函数必须是成员函数或扩展函数,并且:
- 只有一个参数
- 参数不能是可变参数
- 参数不能有默认值
```kotlin
infix fun String.hasPrefix(prefix: String): Boolean = startsWith(prefix)
val ok = "Kotlin" hasPrefix "Kot" // infix 函数有独特写法
```
标准库中的 `to` 也是 infix:
```kotlin
val pair = "name" to "Tom"
```
### inline 函数
`inline` 会把函数体和 lambda 参数内联到调用处,减少 lambda 对象开销,并允许非局部返回。
```kotlin
inline fun <T> withLock(lock: Lock, block: () -> T): T {
lock.lock()
try {
return block()
} finally {
lock.unlock()
}
}
```
`noinline`:不内联某个 lambda 参数,可把它当对象传递。
```kotlin
inline fun foo(noinline block: () -> Unit) {
val f = block
f()
}
```
`crossinline`:内联 lambda 中禁止非局部返回。
```kotlin
inline fun foo(crossinline block: () -> Unit) {
Runnable { block() }.run()
}
```
### 泛型 reified
普通泛型有类型擦除,不能直接 `is T` 或 `T::class`。
配合 `inline` 和 `reified` 可以保留类型信息。
```kotlin
inline fun <reified T> Any.isInstanceOf(): Boolean = this is T
inline fun <reified T> List<*>.filterIsInstance(): List<T> {
val result = mutableListOf<T>()
for (item in this) {
if (item is T) result.add(item)
}
return result
}
```
`reified` 只能用于 `inline` 函数。
### 扩展函数
扩展函数可以在不修改原类的情况下,为类增加函数。
```kotlin
fun String.lastChar(): Char = this[length - 1]
val String.lastChar: Char
get() = this[length - 1]
```
扩展函数是独立于类定义的,使用时也需要额外的 `import`。
```kotlin
open class A
class B : A()
fun A.foo() = "A"
fun B.foo() = "B"
val a: A = B()
println(a.foo()) // A
```
成员函数优先于扩展函数:
```kotlin
class C {
fun foo() = "member"
}
fun C.foo() = "extension"
println(C().foo()) // member
```
#### 概念与 lambda 变量类型
普通函数类型:
```kotlin
val f1: (String) -> Int = { it.length }
```
带接收者的函数类型:
```kotlin
val f2: String.() -> Int = { length }
println("abc".f2()) // 3
println(f2("abc")) // 3
```
区别:
- `(String) -> Int`:参数在 lambda 中用 `it` 或命名参数访问
- `String.() -> Int`:接收者在 lambda 中用 `this` 访问,而且,相当与直接在类 `String` 中定义函数
#### 常用的标准库扩展函数
##### apply