文档知识库教程OpenHarmony【免费下载链接】CangjieCorpus本仓库为仓颉编程语言语料库聚焦 仓颉编程语言 核心知识体系整合官方开发指南、API 文档、典型代码示例及语法规范等多维度资源经结构化处理与专业标注构建为支持 RAG检索增强生成技术 的高质量知识基座适配智能编码助手、学习辅助工具等应用开发需求。 欢迎开发者基于本语料库构建仓颉语言智能应用推动鸿蒙开发生态智能化升级项目地址https://gitcode.com/Cangjie/CangjieCorpus点击查看免费下载扩展extend是仓颉编程语言中为既有类型如class、struct、Int64等内置类型追加成员函数、属性、操作符重载甚至实现接口的核心机制在不破坏被扩展类型封装性的前提下增强其能力。本文围绕官方开发指南中的《访问规则》章节系统讲解扩展自身的修饰符限制、扩展成员的可见性控制、孤儿扩展禁令、访问与遮盖语义以及跨包的导入导出规则并结合 扩展概述、直接扩展、接口扩展 与 顶层声明的可见性 等章节做纵深印证。读完本文你将能准确判断某个扩展成员在哪个包、哪个模块、哪个文件里可见规避孤儿扩展与成员遮盖等编译错误并正确设计跨包扩展的导出边界。先理解两个前提扩展的形态与可见性体系要理解扩展的访问规则需要先建立两个背景知识扩展的两种形态。根据扩展有没有实现新的接口扩展分为直接扩展不包含额外接口与接口扩展包含接口用于为既有类型实现新接口。扩展可以为当前package中可见的类型除函数、元组、接口外添加成员函数、操作符重载函数、成员属性或实现接口但它不能增加成员变量扩展中的函数和属性必须拥有实现也不能破坏被扩展类型的封装性详见 扩展概述。访问修饰符的级别体系。扩展成员的可见性与普通成员一样受四个访问修饰符约束。仓颉的访问级别排序为public protected internal private其中internal是绝大多数顶层声明的默认修饰符且一个声明的访问修饰符不得高于该声明中用到的类型的访问修饰符级别详见 顶层声明的可见性。下表是四个修饰符在不同作用域下的可见性矩阵修饰符文件包及子包模块所有包privateYNNNinternalYYNNprotectedYYYNpublicYYYY需要特别留意的是扩展本身即extend声明这一层与普通顶层声明不同扩展本身不能使用任何访问修饰符修饰扩展的可见性完全由成员修饰符、被扩展类型与泛型约束的修饰符共同决定。这正是本文要展开的核心。扩展的修饰符扩展本身不能使用修饰符扩展声明本身不允许任何访问修饰符前缀。例如下面的代码对A的直接扩展前使用了public修饰编译时将报错public class A {} public extend A {} // Error, expected no modifier before extend编译器给出的错误信息为expected no modifier before extend即extend之前不允许出现任何修饰符。扩展成员可使用的修饰符扩展内部成员可使用的修饰符有六种static、public、protected、internal、private、mut其语义与类型定义处的成员完全一致修饰符可见性 / 行为说明private成员只能在本扩展内使用外部不可见internal成员可在当前包及子包包括子包的子包内使用这是默认行为省略修饰符时即为此语义protected成员在本模块内可被访问受导出规则限制当被扩展类型是class时该class的子类定义体内也能访问static成员只能通过类型名访问不能通过实例对象访问public成员对外可见受后续导出规则约束mut仅对struct类型的扩展允许定义mut函数下面的示例综合展示了各修饰符的使用与访问效果。f1是public、f2是protected、f3是private、f4是staticpackage p1 public open class A {} extend A { public func f1() {} protected func f2() {} private func f3() {} static func f4() {} } main() { A.f4() // Okstatic 成员通过类型名访问 var a A() a.f1() // Okpublic 成员 a.f2() // Okprotected 成员在模块内可访问 }值得注意的是static成员的访问约束只能以A.f4()的形式通过类型名调用a.f4()这种实例访问是不允许的。而mut修饰符与struct可变语义绑定只有对struct类型的扩展才能定义mut函数参见 struct 的可变性。扩展成员不支持 open / override / redef扩展是追加而非改写因此扩展内的成员定义不支持使用open、override、redef修饰——既不能声明可被进一步重写的成员也不能重写或重定义被扩展类型中的既有成员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 }这正是扩展不能变更被扩展类型封装性的体现扩展只做加法不做改写。扩展的孤儿规则为一个其他package的类型实现另一个package的接口可能造成理解上的困扰。为了防止一个类型被意外实现不合适的接口仓颉不允许定义孤儿扩展——即既不与接口包含接口继承链上的所有接口定义在同一个包中也不与被扩展类型定义在同一个包中的接口扩展。具体来说如下代码所示不能在package c中为package a里的Foo实现package b里的Bar只能在package a或者在package b中为Foo实现Bar// package a public class Foo {} // package b public interface Bar {} // package c import a.Foo import b.Bar extend Foo : Bar {} // Error这条规则的约束对象是接口扩展。孤儿规则的判定还会把接口继承链上的所有接口一并纳入即如果目标接口继承自其他接口那么扩展所在包还必须与这些祖先接口中的某一个或与被扩展类型处于同一包否则同样构成孤儿扩展。扩展的访问与遮盖this的使用与super的限制扩展的实例成员与类型定义处一样可以使用thisthis的功能保持一致同样也可以省略this直接访问成员但扩展的实例成员不能使用super扩展并不参与被扩展类型的继承体系没有父类概念class A { var v 0 } extend A { func f() { print(this.v) // Ok print(v) // Ok } }扩展不能访问被扩展类型的 private 成员扩展虽然与被扩展类型看起来融为一体但本质上仍是外部追加因此不能访问被扩展类型中private修饰的成员。protected成员则可以访问class A { private var v1 0 protected var v2 0 } extend A { func f() { print(v1) // Error print(v2) // Ok } }这与扩展概述中扩展不能访问被扩展类型中private修饰的成员的限制一致见 扩展概述。遮盖规则只能追加不能遮蔽扩展不能遮盖被扩展类型的任何成员。如果扩展中定义的函数与类型自身成员同名同签名编译报错class A { func f() {} } extend A { func f() {} // Error }同样扩展也不允许遮盖其他扩展增加的任何成员。对同一类型的多个扩展之间不能定义相同签名的方法class A {} extend A { func f() {} } extend A { func f() {} // Error }同一包内可以多次扩展在同一个包内对同一类型可以扩展多次并且在扩展中可以直接调用被扩展类型的其他扩展中非private修饰的函数class Foo {} extend Foo { // OK private func f() {} func g() {} } extend Foo { // OK func h() { g() // OK另一个扩展中非 private 的函数可直接调用 f() // Errorprivate 成员仅在本扩展内可见 } }注意private的边界f()被声明在第一个扩展内因此仅在该扩展内部可见即使同包、同一类型的其他扩展也无法调用它。泛型扩展之间的可见性规则扩展泛型类型时可以使用额外的泛型约束where子句详见 泛型约束。泛型类型的任意两个扩展之间的可见性规则如下约束相同两个扩展相互可见即两个扩展内可以直接使用对方内的函数或属性约束不同但存在包含关系约束更宽松的扩展对约束更严格的扩展可见反之约束更严格的扩展对约束更宽松的扩展不可见约束不同且不存在包含关系两个扩展均互相不可见。例如假设对同一个类型EX有两个扩展扩展 1 中X的约束比扩展 2 中更严格X : B而B : A那么扩展 1 中的函数和属性对扩展 2 均不可见反之扩展 2约束更宽松X : A中的函数和属性对扩展 1 可见open class A {} class B : A {} class EX {} interface I1 { func f1(): Unit } interface I2 { func f2(): Unit } extendX EX : I1 where X : B { // extension 1约束更严格 public func f1(): Unit { f2() // OK扩展 2 约束更宽松对扩展 1 可见 } } extendX EX : I2 where X : A { // extension 2约束更宽松 public func f2(): Unit { f1() // Error扩展 1 约束更严格对扩展 2 不可见 } }这条规则可以理解为约束更宽松的扩展覆盖了约束更严格的扩展的能力范围因此更宽松的扩展中定义的成员能够被更严格约束的扩展直接复用反向则不允许。扩展的导入导出扩展也是可以被导入和导出的但扩展本身不能使用可见性修饰符修饰因此扩展的导出遵循一套特殊规则。理解这套规则时导出的核心判定要素是被扩展类型、泛型约束如果有以及对接口扩展而言接口类型的访问修饰符——规则本质上是取这些要素中最低的访问级别来决定扩展是否对外可见与 顶层声明的可见性 中声明不得高于所用类型的访问级别一脉相承。直接扩展的导出规则对于直接扩展分两种情况扩展与被扩展类型在同一个包中扩展是否导出由被扩展类型与泛型约束如果有的访问修饰符同时决定。当所有的泛型约束都是导出类型即public或按导出规则可导出时该扩展将被导出。扩展与被扩展类型不在同一个包中该扩展不会导出。以下示例定义在package a.b中Foo是导出的public类而I1/I2/I3分别是private/internal/protected的接口以此展示泛型约束访问级别如何决定扩展导出与否// package a.b package a.b private interface I1 {} internal interface I2 {} protected interface I3 {} extend Int64 : I1 I2 I3 {} public class FooT {} // The extension will not be exported extendT FooT where T : I1 { public func f1() {} } // The extension will be exported, and only packages that import both Foo and I2 will be able to access it. extendT FooT where T : I2 { public func f2() {} } // The extension will be exported, and only packages that import both Foo and I3 will be able to access it. extendT FooT where T : I3 { public func f3() {} } // The extension will not be exported. The I1 with the lowest access level determines the export. extendT FooT where T : I1 I2 I3 { public func f4() {} } // The extension is exported. Only the package that imports Foo, I2, and I3 can access the extension. extendT FooT where T : I2 I3 { public func f5() {} }上例中f1所在扩展的约束I1是private不可导出故扩展不导出f2、f3所在扩展的约束分别被导出internal/protected均可通过导入访问扩展导出f4所在扩展包含多个约束其中I1未被导出整体不导出f5所在扩展的所有约束均导出故扩展导出。接下来观察不同包中的访问效果。在package a.ca.b的兄弟包与package a.b.da.b的子包中分别访问// package a.c package a.c import a.b.* main() { FooInt64().f1() // Cannot access. FooInt64().f2() // Cannot access. Visible only for sub-pkg. FooInt64().f3() // Ok. FooInt64().f4() // Cannot access. FooInt64().f5() // Cannot access. Visible only for sub-pkg. }// package a.b.d package a.b.d import a.b.* main() { FooInt64().f1() // Cannot access. FooInt64().f2() // Ok. FooInt64().f3() // Ok. FooInt64().f4() // Cannot access. FooInt64().f5() // Ok. }对比两个包的结果可以清晰看出internal与protected的差别internal约束导出的扩展f2在兄弟包a.c中不可访问、在子包a.b.d中可访问而protected约束导出的扩展f3在当前模块内兄弟包与子包均可访问。接口扩展的导出规则接口扩展的导出分为两种情况接口扩展与被扩展类型在相同的package时扩展会与被扩展类型以及泛型约束如果有一起被导出不受接口类型的访问级别影响包外不需要导入接口类型也能访问该扩展的成员。例如在包a中虽然接口I0的访问修饰符为private但Foo的接口扩展仍然会被导出// package a package a private interface I0 {} public class FooT {} // The extension is exported. extendT FooT : I0 {}接口扩展与被扩展类型在不同的package时接口扩展是否导出由接口类型以及泛型约束如果有里用到的类型中最小的访问级别决定。其他package必须导入被扩展类型、相应的接口以及约束用到的类型如果有才能访问对应接口包含的扩展成员。以下示例在package b中为包a的Foo做接口扩展其中接口I1/I2/I3/I4的访问级别分别为private/internal/protected/public// package b package b import a.Foo private interface I1 {} internal interface I2 {} protected interface I3 {} public interface I4 {} // The extension will not be exported because I1 is not visible outside the file. extendT FooT : I1 {} // The extension is exported. extendT FooT : I2 {} // The extension is exported. extendT FooT : I3 {} // The extension is exported extendT FooT : I1 I2 I3 {} // The extension will not be exported. The I1 with the lowest access level determines the export. extendT FooT : I4 where T : I1 I2 I3 {} // The extension is exported. extendT FooT : I4 where T : I2 I3 {} // The extension is exported. extendT FooT : I4 I3 where T : I2 {}归纳可见接口扩展跨包导出遵循两个条件实现接口中至少存在一个导出的接口且所有的泛型约束均可导出。只要存在private级别的接口参与如I1该扩展整体就不会被导出。接口扩展导出的成员仅限于接口包含的成员需要特别强调的是接口扩展导出的成员仅限于接口中包含的成员。扩展中额外添加的、不属于任何所实现接口的成员不会随扩展导出。下面的三包示例验证了这一点以静态库方式编译-p a --output-typestaticlib、-p b --output-typestaticlib最终在package c中链接liba.a libb.a// package a package a public class Foo {}// package b package b import a.Foo public interface I1 { func f1(): Unit } public interface I2 { func f2(): Unit } extend Foo : I1 I2 { public func f1(): Unit {} public func f2(): Unit {} public func f3(): Unit {} // f3 will not be exported }// package c package c import a.Foo import b.I1 main() { let x: Foo Foo() x.f1() // OK, because f1 is a member of I1. x.f2() // error, I2 is not imported x.f3() // error, f3 not found }在package c中只导入了I1f1是I1的成员所以可调用f2属于未导入的I2调用报错f3虽然用public修饰但不在任何已导出接口中同样不可见f3 not found。这提醒开发者跨包场景下想让扩展成员对第三方可见就必须把它们放进接口。扩展的导入规则与导出对应扩展的导入也不需要显式地用import导入扩展本身——只需要导入被扩展的类型、接口和泛型约束就可以导入可访问的所有扩展对直接扩展只需要导入被扩展的类型即可使用该类型对应的扩展成员对接口扩展需要同时导入被扩展的类型、扩展的接口和泛型约束如果有才能使用对应扩展中的成员。以下三个包示例展示了这一差异。package b中只需导入Foo就能调用扩展函数f而package c中要使用接口扩展的成员g必须同时导入Foo与I// package a package a public class Foo {} extend Foo { public func f() {} }// package b package b import a.Foo public interface I { func g(): Unit } extend Foo : I { public func g() { this.f() // OK直接扩展的成员 f 对接口扩展可见 } }// package c package c import a.Foo import b.I func test() { let a Foo() a.f() // OK仅导入 Foo 即可使用直接扩展成员 a.g() // OK同时导入 Foo 与 I 后可使用接口扩展成员 }这个示例还展示了另一个细节在package b的接口扩展实现中this.f()可以直接调用package a中直接扩展定义的f——说明所有通过导入获得的扩展成员会统一并入类型的能力集合扩展与扩展之间可以自由协作但同样受private、遮盖等前述规则约束。小结与实战建议综合全篇仓颉扩展的访问规则可以浓缩为以下几点实践准则扩展自身不带修饰符可见性完全由成员修饰符 被扩展类型 泛型约束的访问级别共同决定设计公共 API 时要把这三个要素的可见性放在一起评估。成员修饰符与普通成员一致默认internal包及子包可见private仅限本扩展protected允许模块内访问且对class子类开放static只能通过类型名访问mut仅适用于struct扩展。只做加法不支持open/override/redef不能遮盖类型或既有扩展的成员不能访问被扩展类型的private成员也不能用super。远离孤儿扩展为他包类型 他包接口做接口扩展必须同时与被扩展类型或接口含继承链同包否则编译报错。泛型扩展可见性看约束约束相同则互见约束有包含关系时宽松者对严格者可见约束无关则互不可见。跨包导出靠最低访问级别决定直接扩展受被扩展类型与约束限制同包且约束全导出才导出接口扩展同包时不受接口级别影响跨包时取接口与约束中最低访问级别且导出成员仅限于接口成员。导入扩展只需导入宿主要素直接扩展导入被扩展类型即可接口扩展还需同时导入接口与泛型约束。在编写可被其他模块复用的扩展库时建议优先使用public接口扩展并显式声明接口成员把扩展的额外成员收敛进接口以确保它们能被稳定地跨包导出和导入。上述全部代码示例均可在本仓库 access_rules.md 中找到配合 直接扩展、接口扩展 与 包的导入 章节阅读效果更佳。赞分享文档知识库教程OpenHarmony【免费下载链接】CangjieCorpus本仓库为仓颉编程语言语料库聚焦 仓颉编程语言 核心知识体系整合官方开发指南、API 文档、典型代码示例及语法规范等多维度资源经结构化处理与专业标注构建为支持 RAG检索增强生成技术 的高质量知识基座适配智能编码助手、学习辅助工具等应用开发需求。 欢迎开发者基于本语料库构建仓颉语言智能应用推动鸿蒙开发生态智能化升级项目地址https://gitcode.com/Cangjie/CangjieCorpus点击查看免费下载相关推荐仓颉编程语言扩展访问规则详解修饰符、孤儿规则与导入导出机制仓颉编程语言扩展访问规则详解修饰符、孤儿规则与导入导出机制 扩展 extend 是仓颉编程语言中为既有类型追加能力的重要机制。本指南聚焦《访问规则》这一主文档编程语言仓颉语言扩展Extension访问规则详解成员修饰符、孤儿规则与跨包导入导出仓颉语言扩展Extension访问规则详解成员修饰符、孤儿规则与跨包导入导出 本文基于仓颉Cangjie官方开发者文档中的「访问规则」一章系统讲解扩文档编程语言仓颉编程语言扩展机制完全指南extend 语法、泛型扩展与访问规则详解仓颉编程语言扩展机制完全指南extend 语法、泛型扩展与访问规则详解 导读 扩展Extension是仓颉编程语言中一项重要的语言机制它允许开发者在 不文档编程语言上一篇别再羡慕别人的毛玻璃任务栏TranslucentTB透明化速成攻略下一篇为什么你的任务栏总挡住壁纸3步玩转TranslucentTB透明化创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考