完成 深入 Kotlin
This commit is contained in:
+396
-4
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user