Kotlin 2.1.0 入门教程(二十四)泛型、泛型约束、绝对非空类型、下划线运算符

泛型类

类可以有类型参数:

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 类型必须同时实现 CharSequenceComparable 接口。

绝对非空类型

为了更方便地与泛型 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)
}
相关推荐
yueqc14 小时前
Kotlin 协程 Flow 操作符总结
kotlin·协程·flow
fanged6 小时前
天马G前端的使用
android·游戏
molong93110 小时前
Kotlin 内联函数、高阶函数、扩展函数
android·开发语言·kotlin
叶辞树11 小时前
Android framework调试和AMS等服务调试
android
慕伏白13 小时前
【慕伏白】Android Studio 无线调试配置
android·ide·android studio
低调小一14 小时前
Kuikly 小白拆解系列 · 第1篇|两棵树直调(Kotlin 构建与原生承载)
android·开发语言·kotlin
跟着珅聪学java14 小时前
spring boot 整合 activiti 教程
android·java·spring
川石课堂软件测试15 小时前
全链路Controller压测负载均衡
android·运维·开发语言·python·mysql·adb·负载均衡
2501_9159214316 小时前
iOS 26 电耗监测与优化,耗电问题实战 + 多工具 辅助策略
android·macos·ios·小程序·uni-app·cocoa·iphone
2501_9159214316 小时前
苹果软件混淆与 iOS 应用加固白皮书,IPA 文件加密、反编译防护与无源码混淆方案全解析
android·ios·小程序·https·uni-app·iphone·webview