重学仓颉-8泛型完全指南:从基础到高级应用

引言

泛型是现代编程语言中最重要的特性之一,它允许我们编写类型安全且可重用的代码。在Cangjie编程语言中,泛型系统设计得既强大又灵活,支持在函数、类、接口、结构体和枚举中使用泛型

泛型基础概念

核心术语

在开始之前,让我们理解几个关键概念:

  • 类型形参(Type Parameter) :在声明时需要指定的类型,用尖括号<>括起
  • 类型变元(Type Variable):在声明体中引用类型形参的标识符
  • 类型实参(Type Argument):使用泛型时指定的具体类型
  • 类型构造器(Type Constructor):需要类型实参来构造具体类型的泛型类型

基本语法

cangjie 复制代码
class List<T> {
    var elem: Option<T> = None
    var tail: Option<List<T>> = None
}

// 泛型函数声明
func sumInt(a: List<Int64>) {}

// 使用泛型类型
var intList: List<Int64> = List<Int64>()

泛型函数

全局泛型函数

全局泛型函数是最常见的泛型使用方式,类型形参紧跟在函数名后:

cangjie 复制代码
// 基本的泛型恒等函数
func id<T>(a: T): T {
    return a
}

// 函数组合的泛型实现
func composition<T1, T2, T3>(f: (T1) -> T2, g: (T2) -> T3): (T1) -> T3 {
    return {x: T1 => g(f(x))}
}

// 使用示例
func times2(a: Int64): Int64 {
    return a * 2
}

func plus10(a: Int64): Int64 {
    return a + 10
}

func times2plus10(a: Int64): Int64 {
    return composition<Int64, Int64, Int64>(times2, plus10)(a)
}

main() {
    println(times2plus10(9))  // 输出: 28
}

局部泛型函数

泛型函数也可以嵌套在其他函数中:

cangjie 复制代码
func foo(a: Int64): Bool {
    // 局部泛型函数
    func id<T>(a: T): T { 
        return a 
    }

    func double(a: Int64): Int64 { 
        return a + a 
    }

    // 利用泛型函数的单位元性质
    return (id<Int64> ~> double)(a) == (double ~> id<Int64>)(a)
}

main() {
    println(foo(1))  // 输出: true
}

泛型成员函数

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

cangjie 复制代码
class A {
    func foo<T>(a: T): Unit where T <: ToString {
        println("${a}")
    }
}

struct B {
    func bar<T>(a: T): Unit where T <: ToString {
        println("${a}")
    }
}

enum C {
    | X | Y

    func coo<T>(a: T): Unit where T <: ToString {
        println("${a}")
    }
}

main() {
    var a = A()
    var b = B()
    var c = C.X
    
    a.foo<Int64>(10)      // 输出: 10
    b.bar<String>("abc")  // 输出: abc
    c.coo<Bool>(false)    // 输出: false
}

扩展中的泛型函数

可以为现有类型添加泛型成员函数:

cangjie 复制代码
extend Int64 {
    func printIntAndArg<T>(a: T): Unit where T <: ToString {
        println(this)        // 打印当前Int64值
        println("${a}")      // 打印泛型参数
    }
}

main() {
    var a: Int64 = 12
    a.printIntAndArg<String>("twelve")
    // 输出:
    // 12
    // twelve
}

静态泛型函数

静态函数也可以是泛型的:

cangjie 复制代码
import std.collection.ArrayList

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

main() {
    var res: ArrayList<Int64> = ArrayList([1, 2, 3, 4])
    var a: (Int64, Int64) = ToPair.fromArray<Int64>(res)
    println("${a[0]}, ${a[1]}")  // 输出: 1, 2
}

泛型类

基本泛型类

泛型类允许我们创建可以处理任意类型的类:

cangjie 复制代码
// 泛型节点类,用于键值对存储
open class Node<K, V> where K <: Hashable & Equatable<K> {
    var key: Option<K> = Option<K>.None
    var value: Option<V> = Option<V>.None

    init() {}

    init(key: K, value: V) {
        this.key = Option<K>.Some(key)
        this.value = Option<V>.Some(value)
    }
    
    func getKey(): Option<K> {
        return key
    }
    
    func getValue(): Option<V> {
        return value
    }
}

// 使用示例
main() {
    var node: Node<String, Int64> = Node<String, Int64>("age", 25)
    match (node.getKey()) {
        case Some(k) => println("Key: ${k}")
        case None => println("No key")
    }
    match (node.getValue()) {
        case Some(v) => println("Value: ${v}")
        case None => println("No value")
    }
}

静态成员限制

泛型类的静态成员不能引用类型参数:

cangjie 复制代码
class A<T> {}

class B<T> {
    static func foo(): Int64 { 
        return 1 
    }
    
    // 错误:静态成员不能依赖泛型参数
    // static var err1: A<T> = A<T>()
    
    // 正确:静态成员使用具体类型
    static var ok: Int64 = 1
}

main() {
    B<Int32>.ok = 2
    println(B<Int64>.ok)  // 输出: 2
}

泛型结构体

基本泛型结构体

结构体也可以使用泛型,类似于泛型类:

cangjie 复制代码
// 泛型二元组结构体
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
    }
}

// 使用示例
main() {
    var a: Pair<String, Int64> = Pair<String, Int64>("hello", 0)
    println(a.first()) // 输出: hello
    println(a.second()) // 输出: 0

    // 不同类型的组合
    var b: Pair<Bool, Float64> = Pair<Bool, Float64>(true, 3.14)
    println("${b.first()}, ${b.second()}") // 输出: true, 3.14
}

泛型枚举

Option类型

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

cangjie 复制代码
// Option类型的简化实现
enum Option<T> {
    | Some(T)
    | None

    public func getOrThrow(): T {
        match (this) {
            case Some(v) => v
            case None => throw NoneValueException()
        }
    }
    
    public func isSome(): Bool {
        match (this) {
            case Some(_) => true
            case None => false
        }
    }
    
    public func isNone(): Bool {
        return !isSome()
    }
}

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

// 使用示例
main() {
    var result1 = safeDiv(10, 2)
    var result2 = safeDiv(10, 0)
    
    match (result1) {
        case Some(v) => println("10 / 2 = ${v}")  // 输出: 10 / 2 = 5
        case None => println("Division by zero")
    }
    
    match (result2) {
        case Some(v) => println("10 / 0 = ${v}")
        case None => println("Division by zero")   // 输出: Division by zero
    }
}

自定义泛型枚举

我们也可以定义自己的泛型枚举:

cangjie 复制代码
// 表示计算结果的泛型枚举
enum Result<T, E> {
    | Success(T)
    | Error(E)
    
    public func isSuccess(): Bool {
        match (this) {
            case Success(_) => true
            case Error(_) => false
        }
    }
    
    public func unwrap(): T {
        match (this) {
            case Success(v) => v
            case Error(_) => throw NoneValueException()
        }
    }
}

// 使用示例
func divide(a: Int64, b: Int64): Result<Int64, String> {
    if (b == 0) {
        return Result<Int64, String>.Error("Division by zero")
    } else {
        return Result<Int64, String>.Success(a / b)
    }
}

main() {
    var result = divide(10, 2)
    if (result.isSuccess()) {
        println("Result: ${result.unwrap()}")  // 输出: Result: 5
    }
}

泛型接口

基本泛型接口

接口也可以使用泛型来定义通用的契约:

cangjie 复制代码
// 可迭代接口
public interface Iterable<E> {
    func iterator(): Iterator<E>
}

// 迭代器接口
public interface Iterator<E> <: Iterable<E> {
    func next(): Option<E>
    func hasNext(): Bool
}

// 集合接口
public interface Collection<T> <: Iterable<T> {
    prop size: Int64
    func isEmpty(): Bool
    func add(item: T): Unit
    func remove(item: T): Bool
}

// 实现示例
class ArrayList<T> <: Collection<T> {
    // 假定这里是底层C函数的数组
    private let items: Array<T> = Array<T>()

    public prop size: Int64 {
        get() {
            return items.size
        }
    }

    public func isEmpty(): Bool {
        return items.isEmpty()
    }

    public func add(item: T): Unit {
        // items.push(item)
    }

    public func remove(item: T): Bool {
        // 简化实现
        return false
    }

    public func iterator(): Iterator<T> {
        return ArrayListIterator<T>(items)
    }
}

// 迭代器实现
class ArrayListIterator<T> <: Iterator<T> {
    private var items: Array<T>
    private var index: Int64 = 0

    init(items: Array<T>) {
        this.items = items
    }

    public func next(): Option<T> {
        if (index < items.size) {
            var item = items[index]
            index = index + 1
            return Option<T>.Some(item)
        } else {
            return Option<T>.None
        }
    }

    public func hasNext(): Bool {
        return index < items.size
    }

    public func iterator(): Iterator<T> {
        return this
    }
}

泛型约束

接口约束

泛型约束允许我们限制类型参数必须实现特定的接口:

cangjie 复制代码
// 可打印接口
public interface ToString {
    func toString(): String
}

// 可比较接口
public interface Comparable<T> {
    func compareTo(other: T): Int32
}

// 使用约束的泛型函数
func genericPrint<T>(a: T): Unit where T <: ToString {
    println(a.toString())
}

// 泛型比较函数
func max<T>(a: T, b: T): T where T <: Comparable<T> {
    if (a.compareTo(b) > 0) {
        return a
    } else {
        return b
    }
}

// 实现接口的类型
class Person <: ToString & Comparable<Person> {
    var name: String
    var age: Int64
    
    init(name: String, age: Int64) {
        this.name = name
        this.age = age
    }
    
    public func toString(): String {
        return "Person(name=${name}, age=${age})"
    }
    
    public func compareTo(other: Person): Int32 {
        if (this.age < other.age) {
            return -1
        } else if (this.age > other.age) {
            return 1
        } else {
            return 0
        }
    }
}

// 使用示例
main() {
    var person1 = Person("Alice", 25)
    var person2 = Person("Bob", 30)
    
    genericPrint<Person>(person1)  // 输出: Person(name=Alice, age=25)
    
    var older = max<Person>(person1, person2)
    println("Older person: ${older.name}")  // 输出: Older person: Bob
}

类类型约束

我们也可以使用具体的类来约束泛型类型:

cangjie 复制代码
import std.collection.ArrayList

// 动物基类
abstract class Animal {
    public func run(): String
    public func makeSound(): String
}

// 狗类
class Dog <: Animal {
    public func run(): String {
        return "dog run"
    }

    public func makeSound(): String {
        return "woof"
    }
}

// 狐狸类
class Fox <: Animal {
    public func run(): String {
        return "fox run"
    }

    public func makeSound(): String {
        return "yip"
    }
}

// 动物园类,只能容纳动物
class Zoo<T> where T <: Animal {
    let animals: ArrayList<Animal> = ArrayList<Animal>()

    public func addAnimal(a: T): Unit {
        animals.add(a)
    }

    public func allAnimalRuns(): Unit {
        for (a in animals) {
            println(a.run())
        }
    }

    public func allAnimalSounds(): Unit {
        for (a in animals) {
            println(a.makeSound())
        }
    }
}

// 使用示例
main() {
    let zoo: Zoo<Animal> = Zoo<Animal>()
    zoo.addAnimal(Dog())
    zoo.addAnimal(Fox())

    println("Animals running:")
    zoo.allAnimalRuns()
    // 输出:
    // Animals running:
    // dog run
    // fox run

    println("Animals making sounds:")
    zoo.allAnimalSounds()
    // 输出:
    // Animals making sounds:
    // woof
    // yip
}

多重约束

一个类型参数可以同时满足多个约束:

cangjie 复制代码
// 可序列化接口
public interface Serializable {
    func serialize(): String
}

// 可反序列化接口
public interface Deserializable<T> {
    static func deserialize(data: String): T
}

// 同时满足多个约束的泛型函数
func processData<T>(data: T): String where T <: ToString & Serializable {
    let str = data.toString()
    let serialized = data.serialize()
    return "String: ${str}, Serialized: ${serialized}"
}

// 实现多个接口的类型
class DataItem <: ToString & Serializable {
    let id: Int64
    let content: String

    init(id: Int64, content: String) {
        this.id = id
        this.content = content
    }

    public func toString(): String {
        return "DataItem(id=${id}, content=${content})"
    }

    public func serialize(): String {
        return "${id}:${content}"
    }
}

// 使用示例
main() {
    let item = DataItem(1, "Hello World")
    let result = processData<DataItem>(item)
    println(result)
    // 输出: String: DataItem(id=1, content=Hello World), Serialized: 1:Hello World
}

泛型子类型关系

基本概念

在Cangjie中,泛型类型的子类型关系遵循特定的规则:

cangjie 复制代码
// 泛型接口
interface I<X, Y> { }

// 泛型类实现泛型接口
class C<Z> <: I<Z, Z> { }

// 使用示例
main() {
    // 根据 class C<Z> <: I<Z, Z>,我们知道:
    // C<Bool> <: I<Bool, Bool>
    // C<Int64> <: I<Int64, Int64>
    
    var c1: C<Bool> = C<Bool>()
    var c2: C<Int64> = C<Int64>()
    
    // 这些类型关系是成立的
}

型变规则

Cangjie中的用户自定义泛型类型在其类型参数处是不型变的:

cangjie 复制代码
open class C { }
class D <: C { }

class I<X> { }

main() {
    // 即使 D <: C 成立,I<D> <: I<C> 也不成立
    // 这是因为Cangjie中的泛型类型是不型变的
    
    var d: D = D()
    var c: C = d  // 这是允许的
    
    // 但是泛型类型之间不能这样转换
    var i1: I<D> = I<D>()
    // var i2: I<C> = i1  // 这会报错
}

内建类型的型变

内建类型遵循不同的型变规则:

cangjie 复制代码
open class C {}

class D <: C {}

main() {
    // 元组类型对其每个元素类型都是协变的
    var tuple1: (D, D) = (D(), D())
    var tuple2: (C, C) = tuple1 // 这是允许的

    // 函数类型在其入参类型处是逆变的,在返回类型处是协变的
    var func1: (C) -> D = {c: C => D()}
    var func2: (D) -> C = func1 // 这是允许的
}

类型别名

基本类型别名

类型别名可以为复杂类型提供更简洁的名称:

cangjie 复制代码
// 基本类型别名
type I64 = Int64
type F64 = Float64
type Str = String

// 使用示例
main() {
    var a: I64 = 42
    var b: F64 = 3.14
    var c: Str = "Hello"
    
    println("${a}, ${b}, ${c}")
}

泛型类型别名

类型别名也可以使用泛型:

cangjie 复制代码
// 泛型结构体
struct RecordData<T> {
    var a: T
    public init(x: T) {
        a = x
    }
}

// 泛型类型别名
type RD<T> = RecordData<T>
type IntRecord = RecordData<Int64>
type StringRecord = RecordData<String>

// 使用示例
main() {
    var struct1: RD<Int32> = RecordData<Int32>(2)
    var struct2: IntRecord = RecordData<Int64>(42)
    var struct3: StringRecord = RecordData<String>("Hello")
    
    println("${struct1.a}, ${struct2.a}, ${struct3.a}")
    // 输出: 2, 42, Hello
}

类型别名的使用场景

类型别名有多种使用方式:

cangjie 复制代码
// 1. 作为类型使用
type A = B
class B {
    static func staticMethod() {}
}
var a1: A = B()

// 2. 作为构造器名称使用
type C = B
func foo() { C() }

// 3. 访问静态成员
type D = B
func bar() {
    D.staticMethod()  // 如果B有静态方法
}

// 4. 枚举构造器
enum TimeUnit {
    | Day | Month | Year
}
type Time = TimeUnit
var day = Time.Day
var month = Time.Month

实际应用案例

通用容器实现

让我们实现一个通用的栈容器:

cangjie 复制代码
import std.collection.ArrayList

// 泛型栈接口
interface Stack<T> {
    func push(item: T): Unit
    func pop(): Option<T>
    func peek(): Option<T>
    func isEmpty(): Bool
    func size(): Int64
}

// 基于ArrayList的栈实现
class ArrayStack<T> <: Stack<T> {
    private var items: ArrayList<T> = ArrayList<T>()

    public func push(item: T): Unit {
        items.add(item)
    }

    public func pop(): Option<T> {
        if (isEmpty()) {
            return Option<T>.None
        } else {
            var size = items.size
            var item = items[size - 1]
            items.remove(at: size - 1)
            return Option<T>.Some(item)
        }
    }

    public func peek(): Option<T> {
        if (isEmpty()) {
            return Option<T>.None
        } else {
            var size = items.size
            return Option<T>.Some(items[size - 1])
        }
    }

    public func isEmpty(): Bool {
        return items.size == 0
    }

    public func size(): Int64 {
        return items.size
    }
}

// 使用示例
main() {
    var intStack: ArrayStack<Int64> = ArrayStack<Int64>()

    // 压入元素
    intStack.push(1)
    intStack.push(2)
    intStack.push(3)

    println("Stack size: ${intStack.size()}")

    // 弹出元素
    while (!intStack.isEmpty()) {
        match (intStack.pop()) {
            case Some(item) => println("Popped: ${item}")
            case None => println("Stack is empty")
        }
    }
}

通用算法实现

实现一些通用的算法函数:

cangjie 复制代码
import std.collection.ArrayList

// 通用查找函数
func find<T>(items: Array<T>, predicate: (T) -> Bool): Option<T> {
    for (item in items) {
        if (predicate(item)) {
            return Option<T>.Some(item)
        }
    }
    return Option<T>.None
}

// 通用过滤函数
func filter<T>(items: Array<T>, predicate: (T) -> Bool): Array<T> {
    let result: ArrayList<T> = ArrayList<T>()
    for (item in items) {
        if (predicate(item)) {
            result.add(item)
        }
    }
    return result.toArray()
}

// 通用映射函数
func map<T, U>(items: Array<T>, transform: (T) -> U): Array<U> {
    let result: ArrayList<U> = ArrayList<U>()
    for (item in items) {
        result.add(transform(item))
    }
    return result.toArray()
}

// 使用示例
main() {
    let numbers: Array<Int64> = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
    
    // 查找第一个偶数
    var firstEven = find<Int64>(numbers, { n: Int64 => n % 2 == 0 })
    match (firstEven) {
        case Some(n) => println("First even number: ${n}")
        case None => println("No even numbers found")
    }
    
    // 过滤出所有偶数
    var evenNumbers = filter<Int64>(numbers, { n: Int64 => n % 2 == 0 })
    println("Even numbers: ${evenNumbers}")
    
    // 将每个数字平方
    var squaredNumbers = map<Int64, Int64>(numbers, { n: Int64 => n * n })
    println("Squared numbers: ${squaredNumbers}")
}

通用数据结构

实现一个通用的二叉树:

cangjie 复制代码
import std.collection.ArrayList
// 泛型二叉树节点
class TreeNode<T> {
    var value: T
    var left: Option<TreeNode<T>> = Option<TreeNode<T>>.None
    var right: Option<TreeNode<T>> = Option<TreeNode<T>>.None
    
    init(value: T) {
        this.value = value
    }
}

// 泛型二叉树
class BinaryTree<T> where T <: Comparable<T> {
    var root: Option<TreeNode<T>> = Option<TreeNode<T>>.None
    
    public func insert(value: T): Unit {
        root = insertRecursive(root, value)
    }
    
    private func insertRecursive(node: Option<TreeNode<T>>, value: T): Option<TreeNode<T>> {
        match (node) {
            case None => return Option<TreeNode<T>>.Some(TreeNode<T>(value))
            case Some(n) =>
                if (value.compare(n.value) == Ordering.LT) {
                    n.left = insertRecursive(n.left, value)
                } else {
                    n.right = insertRecursive(n.right, value)
                }
                return Option<TreeNode<T>>.Some(n)
            
        }
    }
    
    public func inorderTraversal(): Array<T> {
        let result: ArrayList<T> = ArrayList<T>()
        inorderRecursive(root, result)
        return result.toArray()
    }
    
    private func inorderRecursive(node: Option<TreeNode<T>>, result: ArrayList<T>): Unit {
        match (node) {
            case None => return
            case Some(n) => 
                inorderRecursive(n.left, result)
                result.add(n.value)
                inorderRecursive(n.right, result)
            
        }
    }
}

// 使用示例
main() {
    let tree: BinaryTree<Int64> = BinaryTree<Int64>()
    
    // 插入元素
    tree.insert(5)
    tree.insert(3)
    tree.insert(7)
    tree.insert(1)
    tree.insert(9)
    
    // 中序遍历
    let result = tree.inorderTraversal()
    println("Inorder traversal: ${result}")
    // 输出: Inorder traversal: [1, 3, 5, 7, 9]
}

参考资料

相关推荐
在下历飞雨4 小时前
七夕到了,我让AI用Kuikly写了个“孤寡青蛙“App,一码五端真丝滑!
harmonyos
GitCode官方6 小时前
直播预告|鸿蒙原生开发与智能工具实战
华为·harmonyos
Monkey-旭6 小时前
鸿蒙 5.1 深度解析:ArkUI 4.1 升级与分布式开发新范式
分布式·wpf·harmonyos·arkts·openharmony·arkui
北京流年6 小时前
鸿蒙banner页实现
华为·harmonyos
xq95278 小时前
鸿蒙next 游戏授权登录教程王者归来
harmonyos
被开发耽误的大厨19 小时前
鸿蒙ArkUI 基础篇-06-组件基础语法-Column/Row/Text
华为·harmonyos
HarderCoder20 小时前
重学仓颉-7类与接口完全指南:从基础到高级特性
harmonyos
HarderCoder1 天前
重学仓颉-6枚举与模式匹配完全指南
harmonyos
li理1 天前
鸿蒙应用开发完全指南:深度解析UIAbility、页面与导航的生命周期
前端·harmonyos