6634 字
33 分钟
仓颉文档阅读-语言规约VIII: 扩展
NOTE

阅读文档版本:

语言规约 Cangjie-0.53.18-Spec

具体开发指南 Cangjie-LTS-1.0.3

在阅读 了解仓颉的语言规约时, 难免会涉及到一些仓颉的示例代码, 但 我们对仓颉并不熟悉, 所以可以用 仓颉在线体验 快速验证

有条件当然可以直接 配置Canjie-SDK

WARNING

博主在此之前, 基本只接触过C/C++语言, 对大多现代语言都没有了解, 所以在阅读过程中遇到相似的概念, 难免会与C/C++中的相似概念作类比, 见谅

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

扩展#

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

可以添加的功能包括:

  • 添加实例成员函数

  • 添加静态成员函数

  • 添加操作符重载

  • 添加实例成员属性

  • 添加静态成员属性

  • 实现接口

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

  • 扩展不能增加字段

  • 扩展不能增加抽象成员

  • 扩展不能增加open成员

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

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

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

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

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

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

扩展语法#

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

extend String {
func printSize() {
print(this.size)
}
}
"123".printSize() // 3

扩展定义的语法如下:

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, 之后要跟被扩展的类型名, 然后就类似类的定义

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

直接扩展#

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

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

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

接口扩展#

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

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

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

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

func g(i: I) {
i.f()
}
main() {
let a = Foo()
g(a)
}

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

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() {}
}

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

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

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

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, 通过扩展已经实现的接口, 也不能重复实现

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

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

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

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

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

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

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 的默认实现不再使用

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

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

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

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

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

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

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

// 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. 要么 与被扩展类型定义在一个包中

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

    就像这样:

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

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

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

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

    就像这样:

    // 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)

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

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

扩展的成员#

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

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

函数#

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

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

例如:

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修饰的函数只能在本包内使用

// 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修饰

class Foo {}
extend Foo {
static func f() {}
}

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

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

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

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

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函数、不允许重定义原类型父类的静态函数

属性#

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

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

例如:

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

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

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

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

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

// 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修饰

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

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

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

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后允许是一个任意实例化完全的泛型类型

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

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

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

class Foo<T> where T <: ToString {}
extend Foo<Int64> {} // ok
class Bar {}
extend Foo<Bar> {} // error

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

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

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

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

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

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

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

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, 必须要在被扩展类型中使用

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

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

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

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

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

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

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

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
}

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

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

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

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, 不能重复实现同一个接口

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

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

// 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

class A {
var v = 0
}
extend A {
func f() {
print(this.v) // ok
print(v) // ok
}
}

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

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

class A {
private var v1 = 0
protected var v2 = 0
}
extend A {
func f() {
print(v1) // error
print(v2) // ok
}
}

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

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

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

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

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

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

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

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的成员

// 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, 那么扩展的成员会被子类继承

open class A {}
extend A {
func f() {}
}
class B <: A {
func g() {
f() // ok
}
}
main() {
let x = B()
x.f() // ok
}

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

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

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

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时, 如果被扩展类型是导出的, 扩展会与被扩展类型一起被导出, 否则扩展不会被导出

package pkg1
public class Foo {}
extend Foo {
public func f() {}
}
///////
package pkg2
import pkg1.*
main() {
let a = Foo()
a.f() // ok
}

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

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
}

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

接口扩展的导出#

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

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

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

导入扩展#

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

package pkg1
public class Foo {}
extend Foo {
public func f() {}
}
///////
package pkg2
import pkg1.Foo // import Foo
main() {
let a = Foo()
a.f() // ok
}

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

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的扩展并不能直接使用, 而是要 将需要使用的接口扩展的 接口 也导入到包中

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

作者
Humid1ch
发布于
2025-10-11
许可协议
CC BY-NC-SA 4.0