当前位置: 首页 > news >正文

Kotlin泛型约束

泛型类型约束简介

之前我们已经了解了,泛型可以接受任意类型的参数,使代码具有良好的复用性。现在,我们来学习泛型的另一个方面:类型约束(type bounds)
有时候,我们希望对泛型函数或类中允许的类型参数进行限制。例如,我们有一个泛型类 Storage<T>,但我们只想让它存储“书籍”类型的对象,而不需要创建另一个专门的类。这种情况下,就可以使用类型约束。


在类中使用类型约束

假设我们有以下泛型类:

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

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

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

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

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


创建类并使用泛型类

open class Book {}
class Magazine : Book() {}
class Stone {}

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

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

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>),这是不允许的。


在函数中使用类型约束

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

fun <T : Book> sortByDate(list: List<T>) { ... }

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

假设有两个列表:

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 & AnyTAny 的交集类型)。

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

示例 Java 接口:
public interface Game<T> {public T save(T x);@NotNullpublic T load(@NotNull T x);
}

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

Kotlin 中实现:
interface ArcadeGame<T1> : Game<T1> {override fun save(x: T1): T1override fun load(x: T1 & Any): T1 & Any // 正确用法// override fun load(x: T1): T1         // 编译失败
}

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


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

fun <T : String?> elvisLike(first: T, second: T & Any): T & Any = first ?: second

使用示例:

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 子句。

示例:

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 泛型更加强大、类型安全且灵活。

http://www.xdnf.cn/news/1150903.html

相关文章:

  • 多表查询-8-练习总结
  • 数据库练习3
  • Flowable31动态表单-----------------------终章
  • 博图SCL语言中常用运算符使用详解及实战案例(下)
  • OpenCV 官翻 3 - 特征检测 Feature Detection
  • 【无标题】重点阅读——如何在信息层面区分和表征卷曲维度,解析黑洞内部的维度区分机制
  • 《命令行参数与环境变量:从使用到原理的全方位解析》
  • 搭建比分网服务器怎么选数据不会卡顿?
  • lvs原理及实战部署
  • 【I2C】01.I2C硬件连接I2C总线时序图讲解
  • Go语言pprof性能分析指南
  • Temperature 是在LLM中的每一层发挥作用,还是最后一层? LLM中的 Temperature 参数 是怎么计算的
  • 操作系统-分布式同步
  • TCP/UDP协议深度解析(四):TCP的粘包问题以及异常情况处理
  • GaussDB 数据库架构师修炼(六) 集群工具管理-1
  • 异步解决一切问题 |消息队列 |减少嵌套 |hadoop |rabbitmq |postsql
  • 深入解析 Amazon Q:AWS 推出的企业级生成式 AI 助手
  • 【设计模式C#】外观模式(用于解决客户端对系统的许多类进行频繁沟通)
  • LangGraph教程10:LangGraph ReAct应用
  • 访问 gitlab 跳转 0.0.0.0
  • 深入理解设计模式:策略模式的艺术与实践
  • XSS原型与原型链
  • 告别项目混乱:基于 pnpm + Turborepo 的现代化 Monorepo 工程化最佳实践
  • C++控制台贪吃蛇开发:从0到1绘制游戏世界
  • Git 完全手册:从入门到团队协作实战(2)
  • GaussDB union 的用法
  • Maven 依赖管理
  • Java从入门到精通:全面学习路线指南
  • uniapp props、$ref、$emit、$parent、$child、$on
  • MySQL练习3