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)
}
相关推荐
吴声子夜歌6 分钟前
RxJava——操作符详解(四)
android·echarts·rxjava
我是阿亮啊35 分钟前
Android Handler 消息机制之 Looper 深度解析
android·loop·handler·looper
Mr YiRan38 分钟前
Android 16KB 腾讯Mars XLog适配
android
2501_9159214342 分钟前
不用 Xcode 上架 iOS,拆分流程多工具协作完成 iOS 应用的发布准备与提交流程
android·macos·ios·小程序·uni-app·iphone·xcode
子木鑫1 小时前
[SUCTF2019 & GXYCTF2019] 文件上传绕过实战:图片马 + .user.ini / .htaccess 构造 PHP 后门
android·开发语言·安全·php
一起养小猫1 小时前
Flutter for OpenHarmony 实战:打造功能完整的记账助手应用
android·前端·flutter·游戏·harmonyos
_乐无1 小时前
Unity 发布 Android 安卓端所有文件可读写
android·unity·游戏引擎
User_芊芊君子1 小时前
【LeetCode原地复写零】:双指针+逆向填充,O(n)时间O(1)空间最优解!
android·linux·leetcode
2501_944448003 小时前
Flutter for OpenHarmony衣橱管家App实战:支持我们功能实现
android·javascript·flutter
2601_9498333912 小时前
flutter_for_openharmony口腔护理app实战+预约管理实现
android·javascript·flutter