泛型类
类可以有类型参数:
kotlin
class Box<T>(t: T) {
var value = t
}
要创建类实例,只需提供类型实参即可:
kotlin
val box: Box<Int> = Box<Int>(1)
但如果类型参数可以被推断出来,例如从构造函数实参中推断,那么可以省略类型实参:
kotlin
val box = Box(1)
泛型函数
并非只有类声明可以有类型参数,函数也可以有。类型参数要放在函数名之前:
kotlin
fun <T> singletonList(item: T): List<T> {
}
fun <T> T.basicToString(): String {
}
要调用泛型函数,需要在调用处的函数名之后指定类型实参:
kotlin
val l = singletonList<Int>(1)
如果类型实参可以从上下文推断出来,那么就可以省略类型实参:
kotlin
val l = singletonList(1)
泛型约束
kotlin
fun <T : Comparable<T>> sort(list: List<T>) {}
冒号后面指定的类型就是上界,这表明只有 Comparable<T>
的子类型才能替换 T
。
如果没有指定上界,默认的上界是 Any?
。
在尖括号内只能指定一个上界。如果同一个类型参数需要多个上界,就需要使用单独的 where
子句:
kotlin
fun <T> copyWhenGreater(list: List<T>, threshold: T): List<String>
where T : CharSequence, T : Comparable<T>
{
return list.filter { it > threshold }.map { it.toString() }
}
传入的类型必须同时满足 where
子句中的所有条件。在上面的例子中,T
类型必须同时实现 CharSequence
和 Comparable
接口。
绝对非空类型
为了更方便地与泛型 Java
类和接口进行互操作,Kotlin
支持将泛型类型参数声明为绝对非空类型。
若要将泛型类型 T
声明为绝对非空类型,可使用 & Any
来声明该类型,例如:T & Any
。
绝对非空类型必须有一个可空的上界。
声明绝对非空类型最常见的使用场景是,当你想要重写一个 Java
方法,且该方法的参数带有 @NotNull
注解时。例如,考虑 load()
方法:
java
import org.jetbrains.annotations.*;
public interface Game<T> {
public T save(T x) {}
@NotNull
public T load(@NotNull T x) {}
}
要在 Kotlin
中成功重写 load()
方法,你需要将 T1
声明为绝对非空类型:
kotlin
interface ArcadeGame<T1> : Game<T1> {
override fun save(x: T1): T1
override fun load(x: T1 & Any): T1 & Any
}
当仅使用 Kotlin
进行开发时,你不太可能需要显式声明绝对非空类型,因为 Kotlin
的类型推断会为你处理好这一点。
类型实参的下划线运算符
下划线运算符 _
可用于类型实参。
当其他类型已明确指定时,可使用它来自动推断该类型实参:
kotlin
abstract class SomeClass<T> {
abstract fun execute() : T
}
class SomeImplementation : SomeClass<String>() {
override fun execute(): String = "Test"
}
class OtherImplementation : SomeClass<Int>() {
override fun execute(): Int = 42
}
object Runner {
inline fun <reified S: SomeClass<T>, T> run() : T {
return S::class
.java
.getDeclaredConstructor()
.newInstance()
.execute()
}
}
fun main() {
// 由于 SomeImplementation 继承自 SomeClass<String>,
// T 被推断为 String。
val s = Runner.run<SomeImplementation, _>()
assert(s == "Test")
// 由于 OtherImplementation 继承自 SomeClass<Int>,
// T 被推断为 Int。
val n = Runner.run<OtherImplementation, _>()
assert(n == 42)
}