加载中...
仓颉文档阅读-语言规约VIII: 扩展

仓颉文档阅读-语言规约VIII: 扩展

周六 10月 11 2025
6852 字 · 40 分钟

此样式内容, 表示文档原文内容

扩展

扩展可以为除函数、元组、接口外的在当前package可见的任何类型添加新功能

可以添加的功能包括:

  • 添加实例成员函数

  • 添加静态成员函数

  • 添加操作符重载

  • 添加实例成员属性

  • 添加静态成员属性

  • 实现接口

扩展是在类型定义之后追加的功能, 扩展不能破坏原有类型的封装性, 因此以下功能是禁止的

  • 扩展不能增加字段

  • 扩展不能增加抽象成员

  • 扩展不能增加open成员

  • 扩展不能override/redef原有的成员

  • 扩展不能访问原类型的私有成员

扩展, 是C++中没有的概念, C++中类定义完成之后, 就不能再其他地方直接扩展了

从文档描述看, 扩展更多的是为类型添加 成员函数、属性或实现新接口, 禁止对原有成员做覆盖等修改, 也禁止添加新的可继承成员

而且, 扩展不能访问原类型的私有成员

最核心是添加, 但不能修改原类型的一些继承语义

扩展语法

一个简单的扩展示例如下:

CANGJIE
extend String {
    func printSize() {
        print(this.size)
    }
}

"123".printSize()         // 3

扩展定义的语法如下:

PLAINTEXT
extendDefinition
    : 'extend' extendType
      ('<:' superInterfaces)? genericConstraints?
      extendBody
    ;

extendType
    : (typeParameters)? (identifier NL* DOT  NL*)* identifier (NL* typeArguments)?
    | INT8
    | INT16
    | INT32
    | INT64
    | INTNATIVE
    | UINT8
    | UINT16
    | UINT32
    | UINT64
    | UINTNATIVE
    | FLOAT16
    | FLOAT32
    | FLOAT64
    | CHAR
    | BOOLEAN
    | NOTHING
    | UNIT
    ;

extendBody
    : '{' extendMemberDeclaration* '}'
    ;
    
extendMemberDeclaration
    : (functionDefinition
    | operatorFunctionDefinition
    | propertyDefinition
    | macroExpression
    ) end*
    ;

扩展的定义使用extend关键字, 扩展定义依次为extend关键字、可选的泛型形参、被扩展的类型、可选的实现接口、可选的泛型约束, 以及扩展体的定义

扩展体的定义不允许省略{}

扩展只能定义在top level

扩展分为直接扩展和接口扩展两种用法, 直接扩展不需要声明额外的接口

扩展的关键字是extend, 之后要跟被扩展的类型名, 然后就类似类的定义

但要按照扩展要求进行实现

直接扩展

直接扩展不需要声明额外的接口, 可以用来直接为现有的类型添加新功能

CANGJIE
class Foo {}

extend Foo {
    func f() {}
}

main() {
    let a = Foo()
    a.f() // call extension function
}

直接扩展比较简单, 就是按语法添加允许添加的成员就可以了

接口扩展

接口扩展可以用来为现有的类型添加新功能并实现接口, 增强抽象灵活性

使用接口扩展时必须要声明被实现的接口

CANGJIE
interface I {
    func f(): Unit
}

class Foo {}

extend Foo <: I {
    public func f() {}
}

对一个类型使用接口扩展功能实现接口I, 其等价于在类型定义时实现接口I, 但使用范围会受到扩展导入导出的限制, 详细见[扩展的导入导出]

CANGJIE
func g(i: I) {
    i.f()
}

main() {
    let a = Foo()
    g(a)
}

我们可以在同一个扩展内同时实现多个接口, 多个接口之间使用&分开, 接口的顺序没有先后关系

CANGJIE
interface I1 {
    func f1(): Unit
}

interface I2 {
    func f2(): Unit
}

interface I3 {
    func f3(): Unit
}

class Foo {}

extend Foo <: I1 & I2 & I3 {
    public func f1() {}
    public func f2() {}
    public func f3() {}
}

接口扩展, 与类型定义时接口实现基本一致, 需要声明被实现的接口, 可以同时扩展实现多个接口

但扩展的接口, 存在使用范围限制, 在之后有提及

如果被扩展类型已经实现过某个接口, 则 不能通过扩展重复实现该接口, 包含使用扩展实现过的接口

CANGJIE
interface I {
    func f(): Unit
}
 
class Foo <: I {
    func f(): Unit {}
}
extend Foo <: I {}        // error, 不能重复实现接口
 
class Bar {}
extend Bar <: I {
    func f(): Unit {}
}
extend Bar <: I {}        // error, 通过扩展已经实现的接口, 也不能重复实现

接口不允许重复实现, 扩展也同样如此, 因为扩展接口可以等价实现接口

如果被扩展类型已经直接实现 某个非泛型接口, 则不能使用扩展重新实现该接口

如果被扩展类型已经直接实现某个泛型接口, 则不能使用相同的类型参数扩展重新实现该接口

CANGJIE
interface I1 {}

class Foo <: I1 {}

extend Foo <: I1 {}               // error

interface I2<T> {}

class Goo<T> <: I2<Int32> {}

extend Goo<T> <: I2<Int32> {}     // error

extend Goo<T> <: I2<T> {}         // ok

依旧是不允许重复实现接口的原则, 泛型接口的类型变元相同也被看作同一接口类型

如果被扩展的类型已经包含 接口要求的函数, 则接口扩展 不能再重新实现这些函数, 也不会再使用接口中的默认实现

CANGJIE
class Foo {
    public func f() {}
}

interface I {
    func f(): Unit
}

extend Foo <: I {}                // ok

extend Foo {
    public func g(): Unit {
        print("In extend!")
    }
}

interface I2 {
    func g(): Unit {
        print("In interface!")
    }
}

extend Foo <: I2 {}               // ok, I2 中 g 的默认实现不再使用

接口的本质类似一个不拥有成员变量的抽象类

所以类是有可能 本身已经实现了 接口中的函数的, 此时 扩展接口的对应函数, 将不能在扩展时被重新实现

禁止定义孤儿扩展, 孤儿扩展指的是 接口扩展既不与接口(包含接口继承链上的所有接口)定义在同一个包中, 也不与被扩展类型定义在同一个包中

即接口扩展只允许以下两种情况:

  • 接口扩展与类型定义处在同一个包

  • 接口扩展实现的接口, 和接口的继承链上 所有未被 被扩展类型实现的接口, 都必须在同一个包中

其它情况的接口扩展都是不允许定义的

CANGJIE
// package pkg1
public class Foo {}
public class Goo {}

// package pkg2
public interface Bar {}
extend Goo <: Bar {}

// package pkg3
import pkg1.Foo
import pkg2.Bar

extend Foo <: Bar {}          // error

interface Sub <: Bar {}

extend Foo <: Sub {}          // error

extend Goo <: Sub {}          // ok, 'Goo' 在 pkg2 中的继承链上实现了接口 'Bar'

接口扩展的限制, 文档的说明有一些绕

明确一些总结就是:

接口扩展这个动作(定义):

  1. 要么 与被扩展类型定义在一个包中

    意思是, 接口扩展 要与 被扩展的类型 定义在同一个包中

    就像这样:

    CANGJIE
    import pkg1.Iter
    
    class AClass {}
    
    extend AClass <: Iter {}

    保证 接口扩展的定义 与 被扩展类型的定义 在同一个包中

  2. 要么 与 所有 被扩展类型还未实现的接口 定义在同一个包中

    意思是, 接口扩展 要与 所有 被扩展类型还未实现 但需要被扩展实现的接口 定义在同一个包中

    就像这样:

    CANGJIE
    // case 1
    // AClass在另一个包 pkg1 中
    import pkg1.AClass
    
    interface Iter1 {}
    
    interface Iter2 {}
    
    extend AClass <: Iter1 & Iter2 {}       // OK, 接口扩展定义 与 所有未实现的接口 定义在同一个包中
    
    // case 2
    // AClass在包 pkg1 中
    import pkg1.AClass
    // Iter1在包 pkg2 中
    import pkg2.Iter1
    
    interface Iter2 {}
    
    extend AClass <: Iter1 & Iter2 {}       // Error, 接口扩展定义 未与AClass 定义在同一个包中, 也未与 Iter1 定义在一个包中(Iter1是未实现的接口)
    
    // case 3
    // AClass和Iter1 都在包 pkg1 中, 但AClass未实现Iter1
    import pkg1.AClass
    import pkg1.Iter1
    
    interface Iter2 {}
    
    extend AClass <: Iter1 & Iter2 {}       // Error, 接口扩展定义 未与AClass 定义在同一个包中, 也未与 Iter1 定义在一个包中(Iter1是未实现的接口)
    
    // case 4
    // AClass和Iter1 都在包 pkg1 中, 但AClass未实现Iter1
    import pkg1.AClass
    import pkg1.Iter1
    
    interface Iter2 <: Iter1 {}
    
    extend AClass <: Iter2 {}       // Error, 接口扩展定义 未与AClass 定义在同一个包中, 也未与 Iter1 定义在一个包中(Iter1是接口继承链上未实现的接口)
    
    // case 5
    // AClass和Iter1 都在包 pkg1 中, 且AClass已经实现或扩展了Iter1
    import pkg1.AClass
    import pkg1.Iter1
    
    interface Iter2 <: Iter1 {}
    
    extend AClass <: Iter2 {}       // OK, 接口扩展定义 未与AClass 定义在同一个包中, 但与 所有未实现的接口 定义在同一个包中(AClass已经实现了Iter1)

    保证 接口扩展定义 与 所有需要扩展但被扩展类型未实现的接口 定义在同一个包中

那么 孤儿扩展, 就是这两种情况都不符合

扩展的成员

扩展的成员包括: 静态成员函数、实例成员函数、静态成员属性、实例成员属性、操作符重载函数

基本都是函数和属性, 不能扩展变量字段等

函数

扩展可以对被扩展类型添加函数, 这些函数可以是泛型函数, 支持泛型约束, 支持重载, 也支持默认参数和命名参数

这些函数都不能是抽象函数

例如:

CANGJIE
interface I {}
extend Int64 {
    func f<T>(a: T, b!: Int64 = 0) where T <: I {}
    func f(a: String, b: String) {}
}
修饰符

扩展内的函数定义支持使用private, protected(仅限于被扩展类型是class类型)或public修饰

使用private修饰的函数只能在本扩展内使用, 外部不可见

使用protected修饰的成员函数除了能在本包内被访问, 对包外的当前class子类也可以访问

没有使用private, protectedpublic修饰的函数只能在本包内使用

CANGJIE
// file1 在 包p1
package p1

public open class Foo {}

extend Foo {
    private func f1() {}          // ok
    public func f2() {}           // ok
    protected func f3() {}        // ok
    func f4() {}                  // 没有被 private public protected 修饰, 包内可见
}

main() {
    let a = Foo()
    a.f1()                        // error, 无法访问 private 函数
    a.f2()                        // ok
    a.f3()                        // ok
    a.f4()                        // ok
}

// file2 在 包p2
package p2

import p1.*

class Bar <: Foo {
    func f() {
        f1()                      // error, 无法访问 private 函数
        f2()                      // ok
        f3()                      // ok
        f4()                      // error, 无法在其他包访问 default 函数
    }
}

扩展内的函数支持使用static修饰

CANGJIE
class Foo {}

extend Foo {
    static func f() {}
}

struct类型的扩展可以定义mut函数

CANGJIE
struct Foo {
    var i = 0
}

extend Foo {
    mut func f() {                // ok
        i += 1
    }
}

不影响原类型继承语义的函数, 在扩展中都可以被定义、添加

扩展内的函数定义不支持使用openoverrideredef修饰

CANGJIE
class Foo {
    public open func f() {} 
    static func h() {}
}

extend Foo {
    public override func f() {}   // error
    public open func g() {}       // error
    redef static func h() {}      // error
}

扩展内的函数定义, 不允许新增可继承函数、不允许尝试覆盖原类型父类的open函数、不允许重定义原类型父类的静态函数

属性

扩展可以对被扩展类型添加属性

这些属性都不能是抽象属性

例如:

CANGJIE
extend Int64 {
    mut prop i: Int64 {
        get() {
            0
        }
        set(value) {}
    }
}
修饰符

扩展内的属性定义支持使用private, protected(仅限于被扩展类型是class类型)或public修饰

使用private修饰的属性只能在本扩展内使用, 外部不可见

使用protected修饰的属性除了能在本包内被访问, 对包外的当前class子类也可以访问

没有使用private, protectedpublic修饰的属性只能在本包内使用

CANGJIE
// file1 在 包p1
package p1

public open class Foo {}

extend Foo {
    private prop v1: Int64 {          // ok
        get() { 0 }
    }
    public prop v2: Int64 {           // ok
        get() { 0 }
    }
    protected prop v3: Int64 {        // ok
        get() { 0 }
    } 
    prop v4: Int64 {                  // 在本包内可见
        get() { 0 }
    }
}

main() {
    let a = Foo()
    a.v1                              // error, 无法访问 private 函数
    a.v2                              // ok
    a.v3                              // ok
    a.v4                              // ok
}

// file2 在 包p2
package p2

import p1.*

class Bar <: Foo {
    func f() {
        v1                            // error, 无法访问 private 函数
        v2                            // ok
        v3                            // ok
        v4                            // error, 无法在其他包访问 default 函数
    }
}

扩展内的属性支持使用static修饰

CANGJIE
class Foo {}

extend Foo {
    static prop i: Int64 {
        get() { 0 }
    }
}

上面的不影响原类型继承语义的扩展属性的定义, 与属性的正常定义方式被无二致

扩展内的属性定义不支持使用openoverrideredef修饰

CANGJIE
class Foo {
    open prop v1: Int64 {
        get() { 0 }
    }
    static prop v2: Int64 {
        get() { 0 }
    }
}

extend Foo {
    override prop v1: Int64 {             // error
        get() { 0 }
    }
    open prop v3: Int64 {                 // error
        get() { 0 }
    }
    redef static prop v2: Int64 {         // error
        get() { 0 }
    }
}

依旧是禁止扩展openoverrideredef属性

泛型扩展

如果被扩展的类型是泛型类型, 有两种扩展语法可以对泛型类型扩展功能

一种是上面介绍的非泛型扩展

对于泛型类型, 非泛型扩展可以针对特定的 泛型实例化类型 进行扩展

extend后允许是一个任意实例化完全的泛型类型

为这些类型增加的功能只有在类型完全匹配时才能使用

CANGJIE
extend Array<String> {}   // ok
extend Array<Int> {}      // ok

在扩展中, 泛型类型的类型实参 必须符合 泛型类型定义处的约束要求, 否则会编译报错

CANGJIE
class Foo<T> where T <: ToString {}

extend Foo<Int64> {}                  // ok

class Bar {}
extend Foo<Bar> {}                    // error

被扩展类型是泛型类型时, 如果扩展时指定所有的类型参数, 那么扩展方式就与上面介绍的方式保持一致

另一种是在extend后面引入泛型形参的泛型扩展

泛型扩展可以用来扩展 未实例化 或 未完全实例化 的泛型类型

extend后允许声明泛型形参, 这些泛型形参必须被 直接或间接 使用在被扩展的泛型类型上

为这些类型增加的功能只有在 类型和约束完全匹配 时才能使用

CANGJIE
extend<T> Array<T> {}                     // ok

泛型扩展引入的泛型变元必须在被扩展类型中使用, 否则报未使用错误

CANGJIE
extend<T> Array<T> {}                 // ok
extend<T> Array<Option<T>> {}         // ok
extend<T> Array<Int64> {}             // error
extend<T> Int64 <: Equatable<T> {     // error
    ...
}

如果被扩展类型未指定或未指定所有类型参数, 那么扩展语法就为:extend<T> Array<T> {}

要求extend<T>中的T, 必须要在被扩展类型中使用

不管是泛型扩展还是非泛型扩展, 对泛型类型扩展都不能重定义成员和重复实现接口

当泛型扩展的泛型变元被直接使用在被扩展类型的类型实参时, 对应的泛型变元会隐式引入被扩展类型定义时的泛型约束

CANGJIE
class Foo<T> where T <: ToString {}

extend<T> Foo<T> {}                   // 隐式引入 T <: ToString

泛型扩展引入的泛型形参不能用于被扩展类型或被实现的接口, 否则会编译报错

CANGJIE
extend<T> T {} // error
extend<T> Unit <: T {} // error

我们可以在泛型扩展中使用 额外的泛型约束

通过这种方式添加的成员或接口, 只有当该类型的实例在 满足 扩展的泛型约束 时才可以使用, 否则会报错

CANGJIE
class Foo<T> {
    var item: T
    init(it: T) {
        item = it
    }
}

interface Eq<T> {
    func equals(other: T): Bool
}

extend<T> Foo<T> <: Eq<Foo<T>> where T <: Eq<T> {
    public func equals(other: Foo<T>) {
        item.equals(other.item)
    }
}

class A {}
class B <: Eq<B> {
    public func equals(other: B) { true }
}

main() {
    let a = Foo(A())
    a.equals(a)                       // error, A 尚未实现 Eq
    let b = Foo(B())
    b.equals(b)                       // ok, B 实现了 Eq
}

泛型类型不能重复实现同一接口

对于同一个泛型类型, 无论它是否完全实例化, 都不能重复实现同一接口

如果重复实现会在编译时报错

CANGJIE
interface I {}
extend Array<String> <: I {}              // error, 不能重复实现同一个接口
extend<T> Array<T> <: I {}                // error, 不能重复实现同一个接口

interface Bar<T> {}
extend Array<String> <: Bar<String> {}    // error, 不能重复实现同一个接口
extend<T> Array<T> <: Bar<T> {}           // error, 不能重复实现同一个接口

对于同一个泛型类型, 无论它是否完全实例化, 都不能定义相同类型或相同签名的成员

如果重复定义会在编译时报错

CANGJIE
// case 1
extend Array<String> {
    func f() {}                           // error, 不能重复定义
}
extend<T> Array<T> {
    func f() {}                           // error, 不能重复定义
}

// case 2
extend Array<String> {
    func g(a: String) {}                  // error, 不能重复定义
}
extend<T> Array<T> {
    func g(a: T) {}                       // error, 不能重复定义
}

// case 3
extend<T> Array<T> where T <: Int {
    func g(a: T) {}                       // error, 不能重复定义
}
extend<V> Array<V> where V <: String {
    func g(a: V) {}                       // error, 不能重复定义
}

// case 4
extend Array<Int>  {
    func g(a: Int) {}                     // ok
}
extend Array<String>  {
    func g(a: String) {}                  // ok
}

泛型扩展, 会自动引入 泛型类型的类型约束, 且可以额外添加约束

泛型扩展, 对于同一个泛型类型, 无论它是否完全实例化 都不能重复扩展同一接口, 更不能出现扩展函数重复定义的情况

这里的实例化, 指 类型参数

扩展的访问和遮盖

扩展的实例成员与类型定义处一样可以使用this, this的含义与类型定义中的保持一致

同样也可以省略this访问成员

扩展的实例成员不能使用super

CANGJIE
class A {
    var v = 0
}

extend A {
    func f() {
        print(this.v)         // ok
        print(v)              // ok
    }
}

扩展的实例成员可以使用this, 但不能使用super

扩展不能访问被扩展类型的private成员, 其它修饰符修饰的成员遵循可见性原则

CANGJIE
class A {
    private var v1 = 0
    protected var v2 = 0
}

extend A {
    func f() {
        print(v1)             // error
        print(v2)             // ok
    }
}

扩展不允许遮盖被扩展类型的任何成员

CANGJIE
class A {
    func f() {}
}

extend A {
    func f() {}               // error
}

扩展也不允许遮盖被扩展类型的其它扩展中已增加的任何成员

CANGJIE
class A {}

extend A {
    func f() {}
}

extend A {
    func f() {}               // error
}

在同一个package内对同一类型可以扩展任意多次

扩展不允许出现遮盖, 其实也是不会出现遮盖情况, 因为扩展的成员看作与原类型成员同一作用域层级

在扩展中可以直接使用(不加任何前缀修饰)其它对同一类型的扩展中的非private修饰的成员

CANGJIE
class Foo {}

extend Foo {                  // OK
    private func f() {}
    func g() {}
}

extend Foo {                  // OK
    func h() {
        g()                   // OK
        f()                   // Error
    }
}

扩展泛型类型时, 可以使用额外的泛型约束

泛型类型的扩展中能否直接使用其它对同一类型的扩展中的成员, 除了满足上述可访问性规则之外, 还需要满足以下约束规则:

  • 如果两个扩展的约束相同, 则两个扩展中可以直接使用对方的成员

  • 如果两个扩展的约束不同, 且两个扩展的约束有包含关系, 约束更严格的扩展 中可以直接使用 约束更宽松的扩展 中的成员, 反之, 则不可直接使用

  • 当两个扩展的约束不同时, 且两个约束不存在包含关系, 则两个扩展中 不可以直接使用对方的成员

示例: 假设对同一个类型E<X>的两个扩展分别为扩展 1 和扩展 2 , X的约束在扩展 1 中比扩展 2 中更严格, 那扩展 1 中可直接使用扩展2中的成员, 反之, 扩展 2 中不可直接使用扩展 1的成员

CANGJIE
// B <: A
class E<X> {}

interface I1 {
    func f1(): Unit
}
interface I2 {
    func f2(): Unit
}

extend<X> E<X> <: I1 where X <: B {       // 扩展 1
    public func f1(): Unit {
        f2()                              // OK
    }
}

extend<X> E<X> <: I2 where X <: A   {     // 扩展 2
    public func f2(): Unit {
        f1()                              // Error
    }
}

非泛型扩展可以直接使用其他扩展中的非private成员

而泛型扩展, 因为可能存在类型约束, 所以各扩展成员之间的调用遵循一定的限制

扩展的继承

如果被扩展的类型是 class, 那么扩展的成员会被子类继承

CANGJIE
open class A {}

extend A {
    func f() {}
}

class B <: A {
    func g() {
        f()               // ok
    }
}

main() {
    let x = B()
    x.f()                 // ok
}

需要注意的是, 如果在父类中扩展了成员, 由于继承规则限制, 子类中就无法再定义同名成员, 同时也无法覆盖或重新实现(允许函数重载)

CANGJIE
open class A {}

extend A {
    func f() {}
    func g() {}
}

class B <: A {
    func f() {}               // error
    override func g() {}      // error
}

如果在同包内对同一个类型分别扩展实现父接口和子接口, 那么编译器会先检查实现父接口的扩展, 然后再检查实现子接口的扩展

CANGJIE
class Foo {}

interface I1 {
   func f() {}
}
interface I2 <: I1 {
   func g() {}
}

extend Foo <: I1 {}           // first check
extend Foo <: I2 {}           // second check

如果是对class类型的扩展, 那么扩展成员会被继承

但是不能被覆盖、重新实现, 因为扩展成员禁止被open修饰

对同一个类型进行扩展时, 如果 分别扩展实现存在继承关系的接口, 那么 会先检查父接口的函数, 完毕之后再检查子接口的函数

这样可以保证如果子接口覆盖了父接口的成员, 最终扩展实现 会采取子接口的成员

扩展的导入与导出

extend前不允许有修饰符, 扩展只能与被扩展类型或接口一起导入导出

这个意思应该时, 不能单独导入或导出扩展, 而是扩展之后 导入或导出 被扩展的类型或接口

直接扩展的导出

当直接扩展与被扩展类型的定义处在相同的package时, 如果被扩展类型是导出的, 扩展会与被扩展类型一起被导出, 否则扩展不会被导出

CANGJIE
package pkg1

public class Foo {}

extend Foo {
    public func f() {}
}

///////

package pkg2
import pkg1.*

main() {
    let a = Foo()
    a.f()                 // ok
}

当直接扩展与被扩展类型的定义处在不同的package时, 扩展永远不会被导出, 只能在当前package使用

CANGJIE
package pkg1
public class Foo {}

///////

package pkg2
import pkg1.*

extend Foo {
    public func f() {}
}

func g() {
    let a = Foo()
    a.f()                 // ok
}

///////

package pkg3
import pkg1.*
import pkg2.*

main() {
    let a = Foo()
    a.f()                 // error
}

直接扩展要想被导出, 必须要保证直接扩展与被扩展类型在同一包中

接口扩展的导出

当接口扩展与被扩展类型在相同的包时, 对于 外部可见类型 只可以扩展 同样外部可见的接口

CANGJIE
package pkg1
public class Foo {}

interface I1 {
    func f(): Unit
}

extend Foo <: I1 {                    // error
    public func f(): Unit {}
}

public interface I2 {
    func g(): Unit
}

extend Foo <: I2 {                    // ok
    public func g(): Unit {}
}

///////

package pkg2
import pkg1.*

main() {
    let a = Foo()
    a.g()                             // ok
}

当接口扩展与被扩展类型不在相同的包时, 扩展的访问等级 与 相应接口相同

如果扩展多个接口, 扩展的访问等级是public当且仅当所有接口都是public

CANGJIE
package pkg1
public class Foo {}

public class Bar {}

///////

package pkg2

import pkg1.*

interface I1 {
    func f(): Unit
}

public interface I2 {
    func g(): Unit
}

extend Foo <: I1 & I2 {               // 不可外部访问
    public func f(): Unit {}
    public func g(): Unit {}
}

extend Bar <: I2 {                    // 可外部访问
    public func g(): Unit {}
}

对于接口扩展, 如果与被扩展类型定义在同一个包中, 限制public类型只能扩展实现public类型的接口

如果与被扩展类型不在同一个包中, 则扩展的访问等级与扩展实现的接口保持一致, 扩展多个接口时, 接口均为public时, 整个接口扩展才为public

导入扩展

扩展会与被扩展的类型和扩展实现的接口一起被导入, 不能指定导入扩展

CANGJIE
package pkg1
public class Foo {}

extend Foo {
    public func f() {}
}

///////

package pkg2
import pkg1.Foo               // import Foo

main() {
    let a = Foo()
    a.f()                     // ok
}

特别的, 由于扩展不支持遮盖任何被扩展类型的成员, 当导入的扩展发生重定义时将会报错

CANGJIE
package pkg1
public class Foo {}

///////

package pkg2
import pkg1.Foo

public interface I1 {
    func f(): Unit {}
}

extend Foo <: I1 {}           // ok, 可外部访问

///////

package pkg3
import pkg1.Foo

public interface I2 {
    func f(): Unit {}
}

extend Foo <: I2 {}           // ok, 可外部访问

///////

package pkg4
import pkg1.Foo
import pkg2.I1                // error
import pkg3.I2                // error

最后一个例子, 当在pkg4中导入pkg1.Foo并同时导入pkg2.I1pkg3.I2时, 会报错, 禁止遮蔽

从此例中可以得到一个关于接口扩展的语法规则:

当 在接口定义的包中 进行接口扩展时, 完成的扩展默认只能在接口的包中使用

即, 假设包pkg1中存在class Foo, 包pkg2中存在interface I1, 包pkg3中存在interface I2

如果在接口所在的包中, 对Foo进行接口扩展, 那么在其他包中导入Foo时, Foo的扩展并不能直接使用, 而是要 将需要使用的接口扩展的 接口 也导入到包中

即, 至少 被扩展类型、所扩展的接口 同时被导入, 才能访问对应的扩展成员


Thanks for reading!

仓颉文档阅读-语言规约VIII: 扩展

周六 10月 11 2025
6852 字 · 40 分钟