NOTE阅读文档版本:
语言规约 Cangjie-0.53.18-Spec
具体开发指南 Cangjie-LTS-1.0.3
在阅读 了解仓颉的语言规约时, 难免会涉及到一些仓颉的示例代码, 但 我们对仓颉并不熟悉, 所以可以用 仓颉在线体验 快速验证
有条件当然可以直接 配置Canjie-SDK
WARNING博主在此之前, 基本只接触过C/C++语言, 对大多现代语言都没有了解, 所以在阅读过程中遇到相似的概念, 难免会与C/C++中的相似概念作类比, 见谅
此样式内容, 表示文档原文内容
类和接口
任何面向对象的编程语言, 基本都存在类这个概念, C++不例外, 仓颉当然也不例外
但, C++ 中不存在接口这个东西, 接口这个词, 在业务方面听起来不陌生, 但是语言中的接口具体情况并不是非常了解
接口
接口用来定义一个抽象类型, 它不包含数据, 但可以定义类型的行为
一个类型如果声明实现某接口, 并且实现了该接口中所有的成员, 就被称为实现了该接口
接口的成员可以包含实例成员函数、静态成员函数、操作符重载函数、实例成员属性和静态成员属性, 这些成员都是抽象的, 但函数和属性可以定义默认实现
仓颉中的接口, 像是一个只有成员函数、成员属性、操作符重载函数的抽象类, 不包含成员变量
C++中不严格存在接口的概念
接口定义
接口定义的语法
接口的定义使用
interface关键字, 接口定义依次为: 可缺省的修饰符、interface关键字、接口名、可选的类型参数、是否指定父接口、可选的泛型约束、接口体的定义以下示例是一些接口定义:
interface I1 {}public interface I2<T> {}public interface I3<U> {}public interface I4<V> <: I2<V> & I3<Int32> {}接口定义的语法如下:
interfaceDefinition: interfaceModifierList? 'interface' identifiertypeParameters?('<:' superInterfaces)?genericConstraints?interfaceBody;接口只可以定义在
top level接口体的
{}不允许省略interface I {} // {} 不允许被省略
接口的定义语法, 除了关键词是interface之外, 基本与 类的定义语法保持一致
接口的修饰符
在此小节中, 我们将介绍定义接口可以使用的修饰符
访问修饰符
访问修饰符: 默认, 即不使用访问修饰符, 表示只能在包内部访问
也可以使用
public修饰符表示包外部可访问public interface I {} // 可以在包外被访问interface I2 {} // 只能在包内访问
继承性修饰符
继承修饰符: 默认, 即不使用继承修饰符即可表示接口具有
open修饰的语义, 能在任何可以访问interface的位置继承、实现或扩展interface当然, 也可以显式地使用
open修饰符修饰; 也可以使用sealed修饰符表示只能在interface定义所在的包内继承、实现或扩展该interface
sealed已经蕴含了public的语义, 所以定义sealed interface时的public修饰符是可选的继承
sealed接口的子接口或实现sealed接口的类仍可被sealed修饰或不使用sealed修饰若
sealed接口的子接口被public修饰, 且不被sealed修饰, 则其子接口可在包外被继承、实现或扩展;继承、实现
sealed接口的类型可以不被public修饰// package Apackage Apublic sealed interface I1 {} // OKsealed interface I2 {} // OK, 在`sealed`被使用时,`public`是可选的open interface I3 {} // OKinterface I4 {} // OK,`open`是可选的class C1 <: I1 {} // OKpublic open class C2 <: I1 {} // OKpublic sealed class C3 <: I1 {} // OKextend Int64 <: I1 {} // OK// package Bpackage Bimport A.*class S1 <: C2 {} // OKclass S2 <: C3 {} // Error, C3 是 sealed class, 无法在这里继承
再次遇到sealed, 仓颉中, sealed用于限制class、interface和struct的包外可继承性
而public则是赋予他们包外可访问性, 这两个是不冲突的
NOTE仓颉中,
interface与interface之间, 为继承关系, 即 存在interface2继承自interface1而
interface与class或struct之间, 可以为实现关系, 即 存在class或struct实现了interface, 也可以叫做继承
接口成员
接口的成员包括:
在接口体中声明(即在
{}中声明)的成员: 静态成员函数、实例成员函数、操作符重载函数、静态成员属性和实例成员属性从其他接口继承而来的成员
如以下示例中, I2的成员将包括I1中允许被继承的成员
interface I1 {func f(): Unit}interface I2 <: I1 {}接口成员的 ANTLR 如下:
interfaceMemberDeclaration: (functionDefinition|macroExpression|propertyDefinition) end*;
接口的成员只包括 函数或属性成员, 不存在成员变量
接口中的函数
接口中函数的声明与定义
接口中可以包含实例成员函数和静态成员函数, 这些函数与普通实例成员函数和静态成员函数的编写方式一样, 但可以没有函数实现, 这些函数被称为抽象函数
对于拥有实现的抽象函数, 我们称该函数拥有默认实现
以下是包含抽象函数的示例:
interface MyInterface {func f1(): Unit // 不包含默认实现static func f2(): Unit // 不包含默认实现func f3(): Unit { // 包含默认实现return}static func f4(): Unit { // 包含默认实现return}}抽象函数可以拥有命名参数, 但不能拥有参数默认值
interface MyInterface {func f1(a!: Int64): Unit // OKfunc f2(a!: Int64 = 1): Unit // Error, 不能有参数默认值}
仓颉中, 接口的静态成员函数也可以不实现函数体, 为 抽象静态成员函数
接口的成员函数可以有命名形参, 但不能存在默认值
接口中函数和属性的修饰符
接口中定义的函数或属性已经蕴含
public语义, 使用public修饰会产生编译告警不允许使用
protected、internal和private修饰符修饰接口中定义的函数或属性以下是错误的示例:
interface MyInterface {public func f1(): Unit // 不能使用访问修饰符private static func f2(): Unit // 不能使用访问修饰符}使用
static修饰的函数被称为静态成员函数, 可以没有函数体没有函数体的
static函数不能直接使用接口类型调用, 拥有函数体的static函数可以使用接口类型调用当使用
interface类型名直接调用其静态成员函数时, 如果这个函数里面直接或间接调用了接口中(自己或其它接口)没有实现的其它静态函数, 则编译报错interface I {static func f1(): Unitstatic func f2(): Unit {}static func f3(): Unit {f1()}}main() {I.f1() // Error, 不能通过接口类型直接调用I.f2() // OKI.f3() // Error, f1 未实现}接口中的实例成员函数默认具有
open的语义在接口中定义实例成员函数时,
open修饰符是可选的interface I {open func foo1() {} // okfunc foo2() {} // ok}使用
mut修饰的函数是一种特殊的实例成员函数, 可以用于抽象struct类型的可变行为interface I {mut func f(): Unit}
仓颉接口中的成员函数, 默认都是public的, 不能用其他访问修饰符修饰
接口的成员函数, 默认都是open的, 即 默认都被实现
接口的成员函数也可以用mut修饰, 接口本身实际是不需要mut的, 因为接口不存在成员变量, 也就不存在成员变量是否可修改
接口的mut成员函数更多是为了给实现了接口的strcut准备的
接口的static成员函数是可以不实现函数体的, 而类中的static成员函数必须拥有函数体的实现
接口中的成员属性
interface中也可以定义成员属性, 定义成员属性的语法参见[属性]
接口的默认实现
interface中的抽象函数和抽象属性都可以拥有默认实现
interface被其它interface或类型继承或实现的时候, 如果该interface的默认实现没有被重新实现, 则这些默认实现会被拷贝子类型中
实例成员函数的默认实现中可以使用
this,this的类型是当前interface默认实现和非抽象的成员函数一样, 可以访问当前作用域中所有可访问的元素
默认实现是一种语法糖, 可以给实现类型提供默认行为, 子接口可以沿用父接口的默认实现
默认实现不属于继承语义, 因此不能使用
override/redef, 也不能使用super继承接口的子类型为类时, 默认实现保留
open语义, 可以被类的子类重写interface I {func f() { // f: () -> Ilet a = this // a: Ireturn this}}
上述内容中存在一句描述: 默认实现不属于继承语义, 因此不能使用override/redef, 也不能使用super
但经过测试, 在interface的继承链中, override和redef都是可以使用的, 但super无法被使用
所以, 此描述存在混淆 或 存在描述错误, 可能是在强调 不存在继承或实现关系的interface的默认实现不能使用override/redef, 但这是一定的, 好像并不需要特别强调
或者是在说, 接口的继承关系不是传统的继承语义, 因为super无法被使用
接口继承
接口允许继承一个或多个接口
interface I1<T> {}interface I2 {}interface I3<U> <: I1<U> {} // 继承泛型接口interface I4<V> <: I1<Int32> & I2 {} // 继承多个接口
仓颉中, interface可以多继承, 但class不允许多继承, 其中一个重要原因是interface不存在成员变量, 但不是唯一原因
子接口继承父接口时, 会继承父接口的所有成员:
子接口继承父接口时, 对于非泛型接口不能直接继承多次, 对于泛型接口不能使用相同类型参数直接继承多次
例如:
interface I1 {}interface I2 <: I1 & I1 {} // errorinterface I3<T> {}interface I4 <: I3<Int32> & I3<Int32> {} // errorinterface I5<T> <: I3<T> & I3<Int32> {} // ok如果泛型接口
I3在使用时被给了类型参数为Int32, 那么编译器会在类型使用的位置报错:interface I1<T> {}interface I2<T> <: I1<T> & I1<Int32> {} // okinterface I3 <: I2<Int32> {} // errormain() {var a: I2<Int32> // error}
接口的继承关系中, 不允许存在直接继承多个相同接口类型的情况
即, 如果存在I1, 那么不允许存在interface I2 <: I1 & I1 {}, 如果是泛型接口类型, 类型参数也不允许相同, 因为相同类型参数的泛型接口类型实际属于相同类型
子接口中的默认实现
子接口如果继承了 父接口中 没有默认实现的函数或属性, 则在子接口中 允许仅写此函数或属性的声明(当然也允许定义默认实现), 并且函数声明或定义前的
override或redef修饰符是可选的示例如下:
interface I1 {func f1(): Unitstatic func f2(): Unit}interface I2 <: I1 {func f1(): Unit // okstatic func f2(): Unit // ok}interface I3 <: I1 {override func f1(): Unit // okredef static func f2(): Unit // ok}interface I4 <: I1 {func f1(): Unit {} // okstatic func f2(): Unit {} // ok}interface I5 <: I1 {override func f1(): Unit {} // okredef static func f2(): Unit {} // ok}
子接口中的成员函数是可以使用override或redef修饰的
子接口如果继承了 父接口中 有默认实现的函数或属性, 则在子接口中 不允许 仅写此函数或属性的声明而 没有实现, 如果子接口中给出新的默认实现, 那么定义前的
override或redef修饰符是可选的示例如下:
interface I1 {func f1(): Unit {}static func f2(): Unit {}}interface I2 <: I1 {func f1(): Unit // Error, 'f1' 必须有一个新的实现static func f2(): Unit // Error, 'f2' 必须有一个新的实现}interface I3 <: I1 {override func f1(): Unit {} // okredef static func f2(): Unit {} // ok}interface I4 <: I1 {func f1(): Unit {} // okstatic func f2(): Unit {} // ok}
父接口的成员函数或属性, 如果存在默认实现, 则子接口中不允许只声明不实现, 要不就不声明, 要不就要重新实现
如果子接口继承的多个父接口中拥有相同签名成员的默认实现, 子接口必须提供自己版本的新默认实现, 否则会编译报错
interface I1 {func f() {}}interface I2 {func f() {}}interface I3 <: I1 & I2 {} // error, I3 必须实现 f: () -> Unit
子接口如果是多继承, 且不同父接口中存在同签名成员函数且 都有默认实现 或 都无默认实现
子接口就会出现冲突, 子接口实现一个自己的同签名成员函数 可以解决冲突
但, 如果不同父类接口中 存在同函数名、同参数列表 但返回值类型毫无关系的成员函数, 那么冲突无法解决
实现接口
实现接口时的覆盖与重载
一个类型实现一个或多个接口时规则如下:
抽象类以外的类型实现接口时, 必须实现所有的函数、属性
抽象类实现接口时, 允许不实现接口中的函数和属性
实现函数的函数名、参数列表必须与接口中对应函数的相同
实现函数的返回类型 应该与 接口中对应函数的返回类型相同 或者 为其子类型
如果接口中的函数为泛型函数, 则要求实现函数的类型变元约束 比 接口中对应函数更宽松或相同
实现属性的
mut修饰符必须与接口中相应的属性相同实现属性的类型, 必须与接口中对应的属性相同
如果多个接口中 只有同一函数或属性的 一个默认实现, 则实现类型可以 不实现该函数或属性, 使用默认实现
如果多个接口中 包含同一函数或属性的 多个默认实现, 则实现类型 必须要实现该函数或属性, 无法使用默认实现
如果实现类型已经存在 (从父类继承或本类定义) 接口中同一函数或属性的实现, 则不会再使用任何接口中的默认实现
类型在实现接口时, 函数或属性定义前的
override修饰符(或redef修饰符)是可选的, 无论接口中的函数或属性是否存在默认实现
仓颉中, 抽象类可以只实现接口的部分函数或属性, 非抽象类 需要实现接口的所有的函数或属性
抽象类以外的类型实现接口时:
必须对接口中的抽象函数和抽象属性进行实现, 如下示例中的
f1,f3允许不使用接口中的函数默认实现, 如下示例中的
f2抽象类允许不实现接口中的实例成员函数, 如下示例中抽象类
C1并没有实现接口I中的f1interface I {func f1(): Unitfunc f2(): Unit {return}static func f3(): Int64}class C <: I {public func f1(): Unit {}public func f2(): Unit {return}public static func f3(): Int64 {return 0}}abstract class C1 <: I {public static func f3(): Int64 {return 1}}示例: 接口
I中的函数f和g为泛型函数,class E和F满足实现函数的类型变元约束 比 被实现函数的更宽松或相同的要求, 编译成功;class D不满足要求, 则编译报错// C <: B <: Ainterface I {static func f<T>(a: T): Unit where T <: Bstatic func g<T>(): Unit where T <: B}class D <: I {public static func f<T>(a: T) where T <: C {} // Error, 约束过于严格public static func g<T>() where T <: C {} // Error, 约束过于严格}class E <: I {public static func f<T>(a: T) where T <: A {} // OK, 约束更宽松public static func g<T>() where T <: A {} // OK, 约束更宽松}class F <: I {public static func f<T>(a: T) where T <: B {} // OK, 相同的约束public static func g<T>() where T <: B {} // OK, 相同的约束}更多例子:
// case 1interface I1 {func f(): Unit}interface I2 {func f(): Unit}class A <: I1 & I2 {public func f(): Unit {} // ok}// case 2interface I1 {func f(): Unit}interface I2 {func f(): Unit {}}open class A {public open func f(): Unit {} // ok}class B <: A & I1 & I2 {public override func f(): Unit {} // ok}// case 3interface I1 {func f(): Unit}interface I2 {func f(): Unit {}}class A <: I1 & I2 {} // ok, f from I2// case 4interface I1 {func f(): Unit {}}interface I2 {func f(): Unit {}}class A <: I1 & I2 {} // errorclass B <: I1 & I2 { // ok,public func f(): Unit {}}// case 5interface I1 {func f(a: Int): Unit {}}interface I2 {func f(a: Int): Unit {}}open class A {public open func f(a: Int): Unit {}}open class B <: A & I1 & I2 { // ok, f from A}
对于泛型interface的成员函数, 在子类或子接口中实现时, 类型变元约束不能变得更严格
实现接口时函数重载的规则
父作用域函数与子作用域函数的函数名必须相同, 但参数列表必须不同
以下几个例子中给出了类型实现接口时构成函数重载的一些情形, 需要注意的是 当类型实现接口时 需要为被重载的函数声明提供实现
示例一: 分别在
I1和I2中声明了函数名相同、参数列表不同的函数f需要在实现类
C中同时实现参数类型为Unit和参数类型为Int32的finterface I1 {func f(): Unit}interface I2 {func f(a: Int32): Unit}class C <: I1 & I2 {public func f(): Unit {} // I1 中的 f 需要实现public func f(a: Int32): Unit {} // I2 中的 f 需要实现}示例二: 分别在
I1和I2中定义了函数相同、参数列表不同的默认函数f不需要在
C中实现finterface I1 {func f() {}}interface I2 {func f(a: Int32) {}}class C <: I1 & I2 {}示例三: 在
I1中声明了函数名为f, 参数类型为Unit的函数; 在I2中定义了函数名为f, 参数类型为Int32的函数类
C中必须实现参数类型为Unit的函数finterface I1 {func f(): Unit}interface I2 {func f(a: Int32):Unit {return}}class C <: I1 & I2 {public func f(): Unit {return}}
Any接口
Any接口是一个语言内置的空接口, 所有interface类型都默认继承它, 所有非interface类型都默认实现它, 因此所有类型都可以作为Any类型的子类型使用class A {}struct B {}enum C { D }main() {var i: Any = A() // oki = B() // oki = C.D // oki = (1, 2) // oki = { => 123 } // okreturn 0}在类型定义处可以显式声明实现
Any接口, 如果没有则会由编译器隐式实现, 但不能使用扩展重新实现Any接口class A <: Any {} // okclass B {} // 隐式实现 Anyextend B <: Any {} // error
Any之于接口, 类似Object之于类
不过Any也是被所有类型实现的接口