【笔记】Kotlin的泛型

前言

Kotlin的泛型学习笔记

类中定义泛型

1
2
3
4
5
6
7
class 类名<T> {
...
}

fun main() {
var 对象名 = 类名<泛型类型>()
}
1
abstract class 类名<T> {}
1
interface 接口名<T> {}

泛型作为属性类型

1
2
3
class 类名<T> {
var 变量名: T = 值
}

泛型作为方法形参类型

1
2
3
class 类名<T> {
fun 方法名(形参: T) {}
}

泛型作为方法返回值类型

1
2
3
class 类名<T> {
fun 方法名(): T {}
}

定义多个泛型

1
2
3
4
5
class 类名<T, R> {}

fun main() {
var 对象名 = 类名<泛型类型1, 泛型类型2>()
}
  • 创建对象时忽略部分泛型类型
1
2
3
4
5
class 类名<T, R> {}

fun main() {
var 对象名 = 类名<_, 泛型类型2>()
}

子类明确泛型类型

1
2
3
4
5
6
7
open class 类名<T> {
...
}

class 子类名 : 类名<泛型类型> {
...
}

子类继承泛型类型

1
2
3
4
5
6
7
open class 类名<T> {
...
}

class 子类名<T> : 类名<T> {
...
}

函数中定义泛型

1
2
3
4
5
fun <T> 函数名() {}

fun main() {
函数名<泛型类型>()
}

泛型作为函数形参类型

  • 自动类型推导,省略显式指定函数泛型类型
1
2
3
4
5
6
7
fun <T> 函数名(形参: T) {}

fun main() {
函数名<泛型类型>(实参)

函数名(实参)
}

泛型作为函数返回值类型

1
fun <T> 函数名(): T {}

泛型作为传入函数作为形参的类型

1
fun <T> 函数名(形参名: (T) -> Unit) {}
1
fun <T> 函数名(形参名: () -> T) {}
  • 泛型作为扩展函数
1
fun <T> 函数名(形参名: T.() -> Unit) {}

抗变、逆变、协变

抗变(Invariant)

  • 不通过关键字标记泛型类型时为抗变,抗变父子类不能相互转换
1
var 变量名: 数据类型<泛型类型> = 值

逆变

  • 通过out关键字标记的泛型类型为逆变,逆变可以实现子类到父类的转换
1
2
var 变量名1: 数据类型<out 泛型类型> = 值
var 变量名2: 数据类型<泛型类型父类> = 变量名1

协变

  • 通过in关键字标记的泛型类型为协变,协变可以实现父类到子类的转换
1
2
var 变量名1: 数据类型<in 泛型类型> = 值
var 变量名2: 数据类型<泛型类型子类> = 变量名1

任何类型作为泛型类型

1
var 变量名: 数据类型<*> = 值
1
var 变量名: 数据类型<Any> = 值

为泛型指定类型上限

1
fun <T : 上限类型> 函数名() {}
  • 默认泛型类型上限类型为Any?
1
fun <T> 函数名() {}
1
fun <T : Any?> 函数名() {}

多个泛型

1
2
3
fun <T, *> 函数名() {}

fun <T, out Any?> 函数名() {}
1
2
3
fun <*, T> 函数名() {}

fun <in Nothing, T> 函数名() {}
1
2
3
fun <*, *> 函数名() {}

fun <in Nothing, out Any?> 函数名() {}

类型擦除

  • 泛型只能用于编译前,所以编译后是没有泛型的概念的,泛型T会被编译为Any?,这就是类型擦除

  • 如果泛型存在上界,则类型擦除后会变成上限类型

1
fun <T : 上限类型> 函数名() {}
1
fun <上限类型> 函数名() {}
  • 内联函数由于会在编译时确定泛型类型,所以内联函数不会发生类型擦除
1
inline fun <T> 函数名() {}
1
fun <实际类型> 函数名() {}
  • 内联函数中,可以通过reified具化泛型类型,以便在函数内部将泛型类型当作实际类型来使用
1
2
3
inline fun <reified T> 函数名() {
var t = T()
}

完成

参考文献

哔哩哔哩——青空の霞光