泛型结构体

在仓颉编程语言中,泛型结构体(struct)是一种参数化的复合类型,允许在定义时使用类型参数,从而创建可复用于多种数据类型的结构体。泛型结构体的定义方式与普通结构体类似,但在结构体名称后使用尖括号声明类型参数。

一个典型的泛型结构体定义如下:

 
struct Pair<T, U> { let x: T let y: U public init(a: T, b: U) { x = a y = b } public func first(): T { return x } public func second(): U { return y } } 

在这个例子中,Pair结构体有两个类型参数TU,可以存储任意类型的两个值。使用时需要指定具体的类型参数:

 
var a: Pair<String, Int64> = Pair<String, Int64>("hello", 0) 

泛型结构体与泛型类的主要区别在于:结构体是值类型,而类是引用类型;结构体之间不能继承,而类之间可以继承。结构体的泛型参数同样支持约束,可以使用where子句来限制类型参数必须满足的接口或类约束。

泛型枚举

枚举(enum)在仓颉语言中也可以定义为泛型类型,最典型的例子是Option<T>类型,用于表示可能为空的值:

 
public enum Option<T> { | Some(T) | None } 

Option<T>有两种构造器:Some(T)表示有值的情况,携带一个类型为T的参数;None表示无值的情况。这种设计避免了空指针异常,强制开发者显式处理可能为空的情况。

泛型枚举的使用示例:

 
func safeDiv(a: Int64, b: Int64): Option<Int64> { var res: Option<Int64> = match (b) { case 0 => None case _ => Some(a/b) } return res } 

在这个安全的除法函数中,当除数为0时返回None,否则返回包装在Some中的计算结果。

仓颉语言为Option<T>类型提供了简写形式?T,使得代码更加简洁。例如?Int64等价于Option<Int64>。解构Option值可以使用模式匹配、getOrThrow函数、coalescing操作符(??)等多种方式。

泛型类型的子类型关系

泛型类型实例化后存在子类型关系,但用户自定义泛型类型在其类型参数处默认不型变。这意味着只有当类型参数相等时,泛型类型之间才存在子类型关系。

型变(variance)定义了泛型类型如何随着类型参数的子类型关系而变化:

  • 如果T(A) <: T(B)当且仅当A = B,则T是不型变的(invariant)
  • 如果T(A) <: T(B)当且仅当A <: B,则T是协变的(covariant)
  • 如果T(A) <: T(B)当且仅当B <: A,则T是逆变的(contravariant)

在仓颉语言中,所有用户自定义的泛型类型在其所有的类型参数处都是不变的。例如:

 
open class C { } class D <: C { } interface I<X> { } I<D> <: I<C> // 不成立,即使D <: C成立 

这是因为在仓颉中,用户定义的类型构造器在其类型参数处是不型变的。

这种不型变规则虽然限制了语言表达能力,但同时也避免了如协变数组导致的运行时异常等安全问题。对于需要型变的场景,仓颉提供了内建类型如元组和函数类型,它们具有特定的型变规则:

  • 内建的元组类型对其每个元素类型都是协变的
  • 内建的函数类型在其入参类型处是逆变的,在其返回类型处是协变的

泛型约束

泛型约束用于限制泛型类型参数所具备的操作和能力,确保类型安全。泛型约束可以是接口约束或类类型约束,使用where关键字声明。

语法示例:

 
func genericPrint<T>(a: T) where T <: ToString { println(a) } 

这个泛型函数要求类型参数T必须实现ToString接口,从而可以在函数体内调用toString()方法。

对于更复杂的约束,可以使用&连接多个约束:

 
class Zoo<T> where T <: Animal { var animals: ArrayList<Animal> = ArrayList<Animal>() public func addAnimal(a: T) { animals.add(a) } } 

这个Zoo类要求类型参数T必须是Animal的子类,从而可以调用Animal定义的方法。

总结

仓颉语言的泛型系统提供了强大的抽象能力,同时通过不型变规则和泛型约束确保了类型安全。泛型结构体和泛型枚举允许开发者创建可复用的通用数据结构,而精心设计的子类型关系避免了运行时类型错误。这些特性共同构成了仓颉类型系统的核心,使开发者能够在保证安全性的前提下编写灵活、通用的代码。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐