仓颉语言中的泛型系统:概述、函数、接口与类

一、泛型概述

1.1 泛型核心概念

参数化类型是仓颉泛型的核心,它允许在声明时使用未知类型,在使用时指定具体类型。这种机制在容器类型中尤为常见,如Array<T>Set<T>。泛型通过三个关键概念实现:

  • 类型形参:声明时的占位符类型(如class List<T>中的T
  • 类型变元:在声明体中引用类型形参(如Option<T>中的T
  • 类型实参:使用泛型时指定的具体类型(如List<Int64>中的Int64

1.2 泛型声明位置

仓颉支持在多种结构中使用泛型:

 
// 泛型函数
func id<T>(a: T): T { return a }

// 泛型类
class List<T> {
    var elem: Option<T>
}

// 泛型接口
interface Iterable<E> {
    func iterator(): Iterator<E>
}

// 泛型结构体
struct Pair<T1, T2> {
    let x: T1
    let y: T2
}

// 泛型枚举
enum Option<T> {
    | Some(T)
    | None
}

1.3 类型构造器

需要零个或多个类型作为实参的类型称为类型构造器。例如List是类型构造器,List<Int64>是通过Int64构造出的具体类型。

 

二、泛型函数

2.1 全局泛型函数

在函数名后声明类型形参:

 
func composition<T1, T2, T3>(f: (T1) -> T2, g: (T2) -> T3): (T1) -> T3 {
    return { x: T1 => g(f(x)) }
}

此函数将两个函数组合成新函数,支持任意输入/输出类型组合。

2.2 局部泛型函数

可在其他函数内定义泛型函数:

 
func foo(a: Int64) {
    func id<T>(a: T): T { a }
    println(id<Int64>(10))
}

2.3 成员泛型函数

类、结构体和枚举的成员函数可以是泛型的:

 
class A {
    func foo<T>(a: T) where T <: ToString {
        println("${a}")
    }
}

2.4 静态泛型函数

支持在类、结构体等类型中定义静态泛型函数:

 
class ToPair {
    public static func fromArray<T>(l: ArrayList<T>): (T, T) {
        return (l[0], l
    }
}

2.5 泛型约束

使用where子句约束类型参数:

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

此函数要求T必须实现ToString接口。

三、泛型接口

3.1 基本定义

泛型接口允许在接口中使用类型参数:

 
public interface Iterable<E> {
    func iterator(): Iterator<E>
}

public interface Iterator<E> <: Iterable<E> {
    func next(): Option<E>
}

3.2 接口继承

泛型接口可继承其他接口:

 
public interface Collection<T> <: Iterable<T> {
    prop size: Int64
    func isEmpty(): Bool
}

3.3 实现约束

实现泛型接口时可添加额外约束:

 
class Cell<T> {}

extend <T> Cell<T> <: ToString where T <: ToString {
    public func toString(): String {
        "Cell(value = ${value})"
    }
}

3.4 静态成员抽象

通过泛型约束实现静态成员抽象:

 
interface NamedType {
    static func typename(): String
}

func printTypeName<T>() where T <: NamedType {
    println("Type: ${T.typename()}")
}

四、泛型类

4.1 基本定义

类声明时指定类型参数:

 
public class Node<K, V> where K <: Hashable & Equatable<K> {
    public var key: Option<K>
    public var value: Option<V>
}

4.2 静态成员限制

泛型类的静态成员有特殊限制:

 
class B<T> {
    static var err1: A<T> = A<T>() // 错误!静态成员不能依赖泛型参数
    static var ok: Int64 = 1 // 允许
}

因为静态成员在内存中共享,不能依赖具体类型实例化。

 

4.3 继承与子类型

泛型类可继承其他类或接口:

 
class Zoo<T> where T <: Animal {
    var animals: ArrayList<Animal>
}

4.4 构造器约束

泛型类可约束类型参数:

 
class Node<K, V> where K <: Hashable & Equatable<K> {
    // K必须实现Hashable和Equatable接口
}

五、泛型约束详解

5.1 接口约束

最常见的约束类型:

 
func sort<T>(list: ArrayList<T>) where T <: Comparable<T>

5.2 类类型约束

要求类型是特定类的子类:

 
class Zoo<T> where T <: Animal {
    // T必须是Animal的子类
}

5.3 多约束组合

使用&连接多个约束:

 
func process<T>(item: T) where T <: Serializable & Cloneable

5.4 约束继承

子类可添加额外约束:

 
class SpecialList<T> <: List<T> where T <: Serializable

六、高级泛型特性

6.1 泛型枚举

Option<T>是最常用的泛型枚举:

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

6.2 泛型类型推导

编译器可自动推断类型实参:

 
let list = ArrayList([1, 2, 3]) // 自动推导为ArrayList<Int64>

6.3 泛型反射

运行时获取泛型类型信息:

 
let typeInfo = TypeInfo.of<ArrayList<Int64>>()

6.4 泛型测试

使用@Types宏测试泛型:

 
@Test
@Types[T in <Int64, String>]
class TestClass<T> {
    @TestCase
    func testMethod() { ... }
}

七、最佳实践

7.1 命名规范

  • 单类型参数使用T
  • 多类型参数使用K, V(键值)或T1, T2
  • 约束明确的参数使用有意义的名称(如TElement

7.2 约束设计

  • 仅添加必要的约束
  • 优先使用接口约束而非类约束
  • 避免过度约束限制泛型灵活性

7.3 错误处理

  • 明确处理约束不满足的情况
  • 使用Option<T>处理可能缺失的值
  • 为泛型函数提供清晰的错误信息

结论

仓颉语言的泛型系统提供了强大的抽象能力,通过参数化类型实现了代码复用与类型安全。关键要点包括:

  1. 灵活的应用范围:支持函数、类、接口、结构体和枚举
  2. 严密的类型约束:通过where子句确保类型安全
  3. 静态成员限制:避免泛型参数影响静态成员
  4. 丰富的标准库应用:容器类型完全基于泛型实现
  5. 高级特性支持:反射、测试等场景的深度集成

仓颉的泛型设计平衡了灵活性与安全性,使开发者能够构建高度抽象且类型安全的代码库。掌握泛型的使用约束和最佳实践,是发挥仓颉语言优势的关键。

Logo

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

更多推荐