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 类型必须同时实现 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)
}
相关推荐
千里马学框架5 天前
一起学 Android 14:ShellTransition 屏幕旋转过程深度剖析
android·智能手机·性能优化·framework·性能·屏幕旋转·rotation
美狐美颜SDK开放平台5 天前
开发直播APP时如何接入视频美颜SDK?开发流程与注意事项
android·人工智能·计算机视觉·音视频·直播美颜sdk
AFinalStone5 天前
Android7 SystemUI源码解析(七)Keyguard锁屏模块深度解析
android·systemui
致远ccc5 天前
Google Play 上架前如何测试 App?多国家 Android 环境测试
android·app测试·googleplay·多国家应用测试
ttyyttemo5 天前
Kotlin 协程中的 Job 结构化并发与取消
android
sun0077005 天前
tbox 4g/5g切换,导致wan ip 改变,导致车机旧网络不可用。需要重启车机才行
android
ai2work5 天前
ch23 综合复刻:从零做一个最小可用版本(capstone)
kotlin
其实防守也摸鱼5 天前
内网穿透与反向代理:原理、工具与实战指南
android·大数据·运维·安全·网络安全·自动化·渗透
ai2work5 天前
ch21 签名、校验与发版
kotlin
AFinalStone5 天前
Android7 SystemUI 源码解析(四)NavigationBar 导航栏与 SystemBars
android·systemui