Kotlin泛型约束

泛型类型约束简介

之前我们已经了解了,泛型可以接受任意类型的参数,使代码具有良好的复用性。现在,我们来学习泛型的另一个方面:类型约束(type bounds) 。

有时候,我们希望对泛型函数或类中允许的类型参数进行限制。例如,我们有一个泛型类 Storage<T>,但我们只想让它存储"书籍"类型的对象,而不需要创建另一个专门的类。这种情况下,就可以使用类型约束。


在类中使用类型约束

假设我们有以下泛型类:

kotlin 复制代码
class Storage<T>() {
    // 一些代码
}

解释: 这是一个通用存储类,可以保存任何类型的对象。

如果我们只希望这个类保存书籍,而"书籍"可以包括杂志、手册等,那么我们可以通过添加类型约束 T : Book 来限制只允许 T 是 Book 或其子类:

kotlin 复制代码
class Storage<T : Book>() {
    // 一些代码
}

解释: 通过 T : Book,我们限制了泛型 T 必须是 Book 类型或其子类。这样我们就可以避免误地将非书籍类型存入该类中。


创建类并使用泛型类

kotlin 复制代码
open class Book {}
class Magazine : Book() {}
class Stone {}

解释: 我们创建了三个类:Book 是一个父类,Magazine 继承自 Book,Stone 与 Book 毫无关系。

然后我们尝试如下创建泛型类实例:

kotlin 复制代码
val storage1 = Storage<Book>()       // 合法
val storage2 = Storage<Magazine>()   // 合法(Magazine 是 Book 的子类)
val storage3 = Storage<Stone>()      // 编译错误

解释: 前两个实例是合法的,因为类型满足约束。第三个会报错:类型参数 Stone 不在它的界限内,这是编译时错误,有助于我们提前捕捉潜在问题。
默认情况下,所有泛型类型参数的上界是 Any?(允许空值)。也就是说 SomeGeneric<T> 默认等价于 SomeGeneric<T : Any?>。
作为约束,可以使用类或接口,但不能试图让一个泛型类继承另一个(如 Storage<Magazine> : Storage<Book>),这是不允许的。


在函数中使用类型约束

我们也可以在泛型函数中使用类型约束,语法类似:

kotlin 复制代码
fun <T : Book> sortByDate(list: List<T>) { ... }

解释: 这个函数只接受泛型为 Book 或其子类的 List 参数。

假设有两个列表:

kotlin 复制代码
val listOne: List<Magazine> = listOf()
val listTwo: List<String> = listOf()

sortByDate(listOne) // 合法,Magazine 是 Book 的子类
sortByDate(listTwo) // 错误,String 不是 Book 的子类

明确不可为空的类型(Definitely non-nullable types)

Kotlin 1.7 起支持:明确不可为空的类型 ,用于和 Java 的互操作。语法是:T & Any(T 与 Any 的交集类型)。

前提:类型参数的上界必须是可空类型(如 Any? 或 String?)。

示例 Java 接口:
kotlin 复制代码
public interface Game<T> {
    public T save(T x);

    @NotNull
    public T load(@NotNull T x);
}

解释: Java 中使用了 @NotNull 注解,说明 load() 不允许接收或返回 null。

Kotlin 中实现:
kotlin 复制代码
interface ArcadeGame<T1> : Game<T1> {
    override fun save(x: T1): T1

    override fun load(x: T1 & Any): T1 & Any // 正确用法
    // override fun load(x: T1): T1         // 编译失败
}

解释: 使用 T1 & Any 声明 T1 绝对不能为 null,从而符合 Java 中的 @NotNull 要求。


Kotlin 示例:Elvis 运算符风格函数

kotlin 复制代码
fun <T : String?> elvisLike(first: T, second: T & Any): T & Any = first ?: second

使用示例:

kotlin 复制代码
elvisLike<String>("", "123").length     // 结果为 0
elvisLike<String>("", null).length      // 编译错误,null 不能传给 non-null 参数
elvisLike<String?>(null, "123").length  // 结果为 3
elvisLike<String?>(null, null).length   // 编译错误,null 是非法参数

解释: elvisLike 函数模拟 Elvis 操作符行为:first 可能为 null,但 second 必须是非空的。这样可通过编译时确保安全。


多重约束(Multiple Bounds)

泛型变量可以有多个类型约束 ,但只有一个可以写在 <T> 中,其它的必须使用 where 子句。

示例:

kotlin 复制代码
fun <T> sortByDate(list: List<T>)
    where T : Book, T : Watchable<T> { ... }

解释: 类型参数 T 必须既是 Book 的子类,又实现 Watchable<T> 接口。

注意事项:

  • Kotlin 和 Java 一样 不支持多继承(类只能继承一个父类);

  • 但类可以实现多个接口,因此多个接口约束是允许的。


总结

  • 类型约束用于限制泛型参数类型。

  • 最常见的是 上界约束 (T : SomeType)。

  • 类型约束提高了代码的安全性和可读性。

  • Kotlin 支持:

    • 单个约束

    • 多个接口约束(使用 where 子句)

    • 明确不可为空类型(T & Any)

这使得 Kotlin 泛型更加强大、类型安全且灵活。

相关推荐
叼烟扛炮9 小时前
[LangChain] 学习笔记(06):突破大模型限制——结构化输出(Structured Output)
windows·笔记·学习·langchain
好的佩奇10 小时前
Dart 之扩展类型
android·flutter·dart
alexhilton12 小时前
2027 Android 开发者路线图:学习、面试与 AI
android·kotlin·android jetpack
LongRunning12 小时前
【Linux】RK3568-OpenCV+YOLO(八)
linux·opencv·yolo
灿飞屋14 小时前
Kotlin与Java虚拟机三:控制流
android·java·开发语言·后端·python·kotlin·安卓
疯华整猫15 小时前
我用 Trae 把 Android 内存账重算了一遍
android·性能优化·源码
民乐团扒谱机15 小时前
【微实验】NSGA-II(非支配排序遗传算法)——解决多目标优化问题的“自然选择模拟器”
linux·人工智能·算法
唐青枫15 小时前
Kotlin 对象声明与对象表达式详解
kotlin
물の韜16 小时前
ZYNQ/FMQL系列Linux之I2C开发实战
linux·arm开发·嵌入式硬件·设备树·iic·zynq
qq_3718021116 小时前
Ubuntu Linux中安装WorkBuddy
linux·运维·服务器·ubuntu