NOTE阅读文档版本:
语言规约 Cangjie-0.53.18-Spec
具体开发指南 Cangjie-LTS-1.0.4
在阅读 了解仓颉的语言规约时, 难免会涉及到一些仓颉的示例代码, 但 我们对仓颉并不熟悉, 所以可以用 仓颉在线体验 快速验证
有条件当然可以直接 配置 Canjie-SDK
WARNING博主在此之前, 基本只接触过 C/C++语言, 对大多现代语言都没有了解, 所以在阅读过程中遇到相似的概念, 难免会与 C/C++中的相似概念作类比, 见谅
且, 本系列是文档阅读, 而不是仓颉的零基础教学, 所以如果要跟着阅读的话最好有一门编程语言的开发经验
WARNING在阅读仓颉编程语言的开发指南之前, 已经大概阅读了一遍 仓颉编程语言的语言规约, 已经对仓颉编程语言有了一个大概的了解
所以在阅读开发指南时, 不会对类似: 类、函数、结构体、接口等解释起来较为复杂名称 做出解释
此样式内容, 表示文档原文内容
扩展
访问规则
扩展成员的可访问性, 并不完全与原类型成员的可访问性保持一致
扩展的修饰符
扩展本身不能使用修饰符修饰
例如, 下面的例子中对
A的直接扩展前使用了public修饰, 将编译报错public class A {}public extend A {} // Error, 在 extend 之前不该存在修饰符扩展成员可使用的修饰符有:
static、public、protected、internal、private、mut
使用
private修饰的成员 只能在本扩展内使用, 外部不可见使用
internal修饰的成员 可以在当前包及子包(包括子包的子包)内使用, 这是默认行为使用
protected修饰的成员 在本模块内可以被访问(受导出规则限制)当被扩展类型是
class时, 该class的子类定义体也能访问使用
static修饰的成员, 只能通过类型名访问, 不能通过实例对象访问对
struct类型的扩展 可以定义mut函数package p1public open class A {}extend A {public func f1() {}protected func f2() {}private func f3() {}static func f4() {}}main() {A.f4()var a = A()a.f1()a.f2()}扩展内的成员定义不支持使用
open、override、redef修饰class Foo {public open func f() {}static func h() {}}extend Foo {public override func f() {} // Errorpublic open func g() {} // Errorredef static func h() {} // Error}
扩展定义本身不允许用任何修饰符修饰
但扩展的成员可以使用 可访问性修饰符: private、internal、protected和public, 以及static和mut(仅struct)修饰
同时, 扩展的成员不允许使用open、override和redef修饰, 即扩展不能被继承
TIP
private表示仅在当前扩展内可见即, 仅能在扩展定义时可访问
internal表示仅当前包及子包(包括子包的子包)内可见即, 仅能在当前扩展定义所在包, 以及其子包内访问
protected表示当前模块可见即, 与
internal不同, 被protected修饰的成员, 可以在父包、当前包以及子包访问
public表示模块内外均可见被
public修饰的成员的访问, 只要可以见到, 不受任何限制
扩展的孤儿规则
为一个其他
package的类型实现另一个package的接口, 可能造成理解上的困扰为了防止一个类型被意外实现不合适的接口, 仓颉不允许定义孤儿扩展, 即 既不与接口(包含接口继承链上的所有接口)定义在同一个包中, 也不与被扩展类型定义在同一个包中的接口扩展
如下代码所示, 不能在
package c中, 为package a里的Foo实现package b里的Bar只能在
package a或者在package b中为Foo实现Bar// package apublic class Foo {}// package bpublic interface Bar {}// package cimport a.Fooimport b.Barextend Foo <: Bar {} // Error
关于孤儿扩展, 是只针对 接口扩展的
孤儿扩展是指, 在定义接口扩展时, 在 既不是类型定义的包 也不是接口定义的包中 定义接口扩展
这里的接口, 甚至包含被扩展实现接口继承链上的所有接口
扩展的访问和遮盖
扩展的实例成员与类型定义处一样可以使用
this,this的功能保持一致同样也可以省略
this访问成员扩展的实例成员不能使用
superclass A {var v = 0}extend A {func f() {print(this.v) // Okprint(v) // Ok}}扩展不能访问被扩展类型中
private修饰的成员class A {private var v1 = 0protected var v2 = 0}extend A {func f() {print(v1) // Errorprint(v2) // Ok}}扩展不能遮盖被扩展类型的任何成员
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 { // OKprivate func f() {}func g() {}}extend Foo { // OKfunc h() {g() // OKf() // Error}}
扩展定义时, 可以访问原类型的非private的成员以及this, 也可以访问其他扩展的非private成员, 但无法调用super
扩展定义时, 不允许遮盖 原类型的成员以及其他扩展定义的成员
扩展泛型类型时, 可以使用额外的泛型约束
泛型类型的任意两个扩展之间的可见性规则如下:
如果两个扩展的约束相同, 则两个扩展相互可见, 即两个扩展内可以直接使用对方内的函数或属性
如果两个扩展的约束不同, 且两个扩展的约束有包含关系, 约束更宽松的扩展对约束更严格的扩展可见, 反之, 不可见
当两个扩展的约束不同时, 且两个约束不存在包含关系, 则两个扩展均互相不可见
示例: 假设对同一个类型
E<X>的两个扩展分别为扩展1和扩展2,X的约束在扩展1 中比扩展2 中更严格, 那么扩展1 中的函数和属性对扩展2 均不可见, 反之, 扩展2 中的函数和属性对扩展1 可见open class A {}class B <: A {}class E<X> {}interface I1 {func f1(): Unit}interface I2 {func f2(): Unit}extend<X> E<X> <: I1 where X <: B { // extension 1public func f1(): Unit {f2() // OK}}extend<X> E<X> <: I2 where X <: A { // extension 2public func f2(): Unit {f1() // Error}}
扩展的导入导出
扩展也是可以被导入和导出的, 但是扩展本身不能使用可见性修饰符修饰, 扩展的导出有一套特殊的规则
对于直接扩展, 当扩展与被扩展的类型在同一个包中, 扩展是否导出, 由被扩展类型与泛型约束(如果有)的访问修饰符同时决定, 当所有的泛型约束都是导出类型(修饰符与导出规则, 详见顶层声明的可见性章节)时, 该扩展将被导出
当扩展与被扩展类型不在同一个包中时, 该扩展不会导出
如以下代码所示,
Foo是导出的,f1函数所在的扩展由于不导出泛型约束, 故该扩展不会被导出;f2和f3函数所在的扩展的泛型约束均被导出, 故该扩展被导出;f4函数所在的扩展包含多个泛型约束, 且泛型约束中I1未被导出, 故该扩展不会被导出;f5函数所在的扩展包含多个泛型约束, 所有的泛型约束均是导出的, 故该扩展会被导出// package a.bpackage a.bprivate interface I1 {}internal interface I2 {}protected interface I3 {}extend Int64 <: I1 & I2 & I3 {}public class Foo<T> {}// 该扩展不会被导出extend<T> Foo<T> where T <: I1 {public func f1() {}}// 该扩展会被导出, 只有同时导入 Foo 和 I2 的包才能访问它extend<T> Foo<T> where T <: I2 {public func f2() {}}// 该扩展会被导出, 只有同时导入 Foo 和 I3 的包才能访问它extend<T> Foo<T> where T <: I3 {public func f3() {}}// 扩展不会被导出, 具有最低访问级别的 I1 决定了导出extend<T> Foo<T> where T <: I1 & I2 & I3 {public func f4() {}}// 扩展会被导出, 只有导入 Foo、I2 和 I3 的包才能访问扩展extend<T> Foo<T> where T <: I2 & I3 {public func f5() {}}// package a.cpackage a.cimport a.b.*main() {Foo<Int64>().f1() // 无法访问Foo<Int64>().f2() // 无法访问, 只在子包中可见Foo<Int64>().f3() // OkFoo<Int64>().f4() // 无法访问Foo<Int64>().f5() // 无法访问, 只在子包中可见}// package a.b.dpackage a.b.dimport a.b.*main() {Foo<Int64>().f1() // 无法访问Foo<Int64>().f2() // OkFoo<Int64>().f3() // OkFoo<Int64>().f4() // 无法访问Foo<Int64>().f5() // Ok}
仓颉中, 扩展可以被导入和导出, 但不是由用户手动进行的, 而是根据 与扩展相关的可访问性修饰符 和 扩展定义位置 一起决定, 且由编译器自己决定
当相关类型满足条件时, 扩展自动被导出或导入
针对直接扩展:
-
如果 扩展定义与被扩展类型, 不在同一个包中, 则 该扩展一定不被导出
此时, 扩展就只能在扩展定义时所在的包使用
-
如果 扩展定义拥有泛型约束, 那么扩展的导出 与 被扩展类型和泛型约束的目标类型的可访问性修饰符保持一致
即, 如果泛型约束的目标类型, 被
private修饰, 那么此扩展不被导出如果被
internal修饰, 那么被导出, 且在扩展定义的子包中可访问(被导入)如果被
protected修饰, 那么被导出, 且在扩展定义的所在模块中可访问(被导入)如果被
public修饰, 那么被导出, 且在所有位置可访问(被导入)但, 扩展可访问(扩展导入)的要求是: 泛型约束的所有目标类型 以及 被扩展类型 在目标包中可访问
对于接口扩展则分为两种情况:
当接口扩展与被扩展类型在相同的
package时, 扩展会与被扩展类型以及泛型约束(如果有)一起被导出, 不受接口类型的访问级别影响, 包外不需要导入接口类型也能访问该扩展的成员当接口扩展与被扩展类型在不同的
package时, 接口扩展是否导出由接口类型以及泛型约束(如果有)里用到的类型中最小的访问级别决定其他
package必须导入被扩展类型、相应的接口以及约束用到的类型(如果有), 才能访问对应接口包含的扩展成员如下代码所示, 在包
a中, 虽然接口访问修饰符为private, 但Foo的扩展仍然会被导出// package apackage aprivate interface I0 {}public class Foo<T> {}// 扩展会被导出extend<T> Foo<T> <: I0 {}当在其他包中为
Foo类型扩展时, 扩展是否导出由实现接口和泛型约束的访问修饰符决定实现接口至少存在一个导出的接口, 且所有的泛型约束均可导出时, 该扩展将被导出
// package bpackage bimport a.Fooprivate interface I1 {}internal interface I2 {}protected interface I3 {}public interface I4 {}// 该扩展不会导出, 因为 I1 在文件外部不可见extend<T> Foo<T> <: I1 {}// 该扩展会被导出extend<T> Foo<T> <: I2 {}// 该扩展会被导出extend<T> Foo<T> <: I3 {}// 该扩展会被导出extend<T> Foo<T> <: I1 & I2 & I3 {}// 扩展不会导出, 具有最低访问级别的 I1 将决定导出extend<T> Foo<T> <: I4 where T <: I1 & I2 & I3 {}// 该扩展会被导出extend<T> Foo<T> <: I4 where T <: I2 & I3 {}// 该扩展会被导出extend<T> Foo<T> <: I4 & I3 where T <: I2 {}特别的, 接口扩展导出的成员仅限于接口中包含的成员
// package apackage apublic class Foo {}// package bpackage bimport a.Foopublic 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 不会被导出}// package cpackage cimport a.Fooimport b.I1main() {let x: Foo = Foo()x.f1() // OK, 因为 f1 是 I1 的成员x.f2() // error, f2 没有被导入x.f3() // error, f3 未找到}
针对接口扩展:
-
如果 被扩展类型和接口扩展定义都 在同一个
package中, 那么此扩展与被扩展类型一起被导出, 且与扩展接口的访问限制无关 -
如果 被扩展类型和接口扩展定义 不在同一个
package中-
扩展是否被导出, 与 被扩展类型、扩展的接口 以及泛型约束 中, 所用到类型的最小的可访问性限制决定
-
其他
package如果要访问扩展, 此package需要同时导入 被扩展类型、所有扩展接口类型 以及 泛型约束所用的所有类型
-
-
接口扩展, 只有 被实现接口的成员会被导出, 如果扩展中定义了非实现接口中的成员, 此新成员不会被导出
与扩展的导出类似, 扩展的导入也不需要显式地用
import导入, 扩展的导入只需要导入被扩展的类型、接口和泛型约束, 就可以导入可访问的所有扩展如下面的代码所示, 在
package b中, 只需要导入Foo就可以使用Foo对应的扩展中的函数f而对于接口扩展, 需要同时导入被扩展的类型、扩展的接口和泛型约束(如果有)才能使用
因此在
package c中, 需要同时导入Foo和I才能使用对应扩展中的函数g// package apackage apublic class Foo {}extend Foo {public func f() {}}// package bpackage bimport a.Foopublic interface I {func g(): Unit}extend Foo <: I {public func g() {this.f() // OK}}// package cpackage cimport a.Fooimport b.Ifunc test() {let a = Foo()a.f() // OKa.g() // OK}