NOTE阅读文档版本:
语言规约 Cangjie-0.53.18-Spec
具体开发指南 Cangjie-LTS-1.0.3
在阅读 了解仓颉的语言规约时, 难免会涉及到一些仓颉的示例代码, 但 我们对仓颉并不熟悉, 所以可以用 仓颉在线体验 快速验证
有条件当然可以直接 配置Canjie-SDK
WARNING博主在此之前, 基本只接触过C/C++语言, 对大多现代语言都没有了解, 所以在阅读过程中遇到相似的概念, 难免会与C/C++中的相似概念作类比, 见谅
此样式内容, 表示文档原文内容
属性
属性是一种特殊的语法, 它不像字段一样会存储值, 相反它们提供了一个
getter和一个可选的setter来间接检索和设置值通过使用属性可以将数据操作封装成访问函数, 使用的时候与普通字段无异, 我们只需要对数据操作, 对内部的实现无感知, 可以更便利地实现访问控制、数据监控、跟踪调试、数据绑定等机制
属性在使用时语法与字段一致, 可以作为表达式或被赋值
以下是一个简单的例子,
b是一个典型的属性, 封装了外部对a的访问:class Foo {private var a = 0mut prop b: Int64 {get() {print("get")a}set(value) {print("set")a = value}}}main() {var x = Foo()let y = x.b + 1 // getx.b = y // set}
属性, 是C++中没有的概念
从简单介绍来看, 属性可以用来操作成员变量, 且提供有getter和setter, 用于对属性取值以及赋值
属性的语法
属性的语法规则为:
propertyDefinition: propertyModifier* 'prop' identifier ':' type propertyBody?;propertyBody: '{' propertyMemberDeclaration+ '}';propertyMemberDeclaration: 'get' '(' ')' block end*| 'set' '(' identifier ')' block end*;propertyModifier: 'public'| 'private'| 'protected'| 'internal'| 'static'| 'open'| 'override'| 'redef'| 'mut';
一个属性基本的定义长这样:
class PropTest { mut prop prop1: Int64 { get() {} set() {} }}还可以使用public、open、static等其他修饰符修饰
没有
mut修饰符声明的属性需要定义getter实现用
mut修饰符声明的属性需要单独的getter和setter实现特别是, 对于数值类型、
Bool、Unit、Nothing、Rune、String、Range、Function、Enum和Tuple类型, 在它们的扩展或定义体中不能定义mut修饰的属性, 也不能实现有mut属性的接口如下面的示例所示,
a是没有mut声明的属性,b是使用mut声明的属性class Foo {prop a: Int64 {get() {0}}mut prop b: Int64 {get() {0}set(v) {}}}没有
mut声明的属性没有setter, 而且与用let声明的字段一样, 它们不能被赋值class A {prop i: Int64 {get() {0}}}main() {var x = A()x.i = 1 // error}特别是, 当使用
let声明struct的实例时, 不能为struct中的属性赋值, 就像用let声明的字段一样struct A {var i1 = 0mut prop i2: Int64 {get() {i1}set(value) {i1 = value}}}main() {let x = A()x.i1 = 2 // errorx.i2 = 2 // error}属性与字段不同, 属性不可以赋初始值, 必须要声明类型
属性不是字段, 即 不是变量, 它不能存储值, 不能给初始值, 所以必须要声明类型
虽然不能存储值, 但是要声明类型, 是为了getter返回目标类型数据, 以及setter的传参类型, 即 决定了访问属性时获取的数据类型, 以及赋值时参与计算的数据类型
属性是否用mut修饰, 决定此属性是否需要实现setter以及此属性是否允许被赋值
let实例化的strcut, 即使属性为mut, 也禁止修改属性
基础类型在扩展时, 不能定义mut属性, 也不能实现拥有mut属性的接口, 因为基础类型都是值类型
属性的定义
属性可以在
interface,class,struct,enum,extend中定义class A {prop i: Int64 {get() {0}}}struct B {prop i: Int64 {get() {0}}}enum C {prop i: Int64 {get() {0}}}extend A {prop s: String {get() {""}}}
可以在
interface和abstract class中声明抽象属性, 它的定义体可以省略在实现类型中实现抽象属性时, 它必须保持相同的名称、相同的类型和相同的
mut修饰符interface I {prop a: Int64}class A <: I {public prop a: Int64 {get() {0}}}如同
interface中的抽象函数可以拥有默认实现,interface中的抽象属性也同样可以拥有默认实现拥有默认实现的抽象属性, 实现类型可以不提供自己的实现(必须符合默认实现的使用规则)
interface I {prop a: Int64 { // okget() {0}}}class A <: I {} // ok
接口和抽象类中可以声明抽象属性, 但在子类或子接口中实现时, 必须要与原抽象属性声明保持完全一致
属性分为实例成员属性和静态成员属性
其中, 实例成员属性只能由实例访问, 在
getter或setter的实现中可以访问this、实例成员和其它静态成员而静态成员属性只能访问静态成员
class A {var x = 0mut prop X: Int64 {get() {x + y}set(v) {x = v + y}}static var y = 0static mut prop Y: Int64 {get() {y}set(v) {y = v}}}属性不支持重载, 也不支持遮盖, 不能和其它同级别成员重名
open class A {var i = 0prop i: Int64 { // errorget() {0}}}class B <: A {prop i: Int64 { // errorget() {0}}}
静态属性的getter和setter中 只能访问静态成员
普通属性的getter和setter中 也可以访问静态成员, 同时可以访问this
属性的名字不能与其他成员存在任何冲突, 也不支持重载, 也不支持遮盖, 但是支持覆盖
属性的实现
属性的
getter和setter分别对应两个不同的函数
getter函数类型是()->T,T是该属性的类型, 当使用该属性作为表达式时会执行getter函数
setter函数类型是(T)->Unit,T是该属性的类型, 形参名需要显式指定, 当对该属性赋值时会执行setter函数属性的实现同函数的实现规则一样, 其中可以包含声明和表达式, 可以省略
return, 其返回值必须符合返回类型class Foo {mut prop a: Int64 {get() { // () -> Int64"123" // error}set(v) { // (Int64) -> Unit123}}}无论在属性内部还是外部, 访问属性的行为都是一致的, 因此属性递归访问时与函数一样可能会造成死循环
class Foo {prop i: Int64 {get() {i // dead loop}}}需要注意的是,
struct的setter是mut函数, 因此也可以在setter内部修改其它字段的值, 并且this会受到mut函数的限制
属性声明的类型, 决定了setter和getter函数的类型
禁止在getter函数内访问属性本身, 禁止在setter函数内给属性本身赋值, 会出现无限递归
属性的修饰符
属性跟函数一样可以使用修饰符修饰, 但只允许对整个属性修饰, 不能对
getter或setter独立修饰class Foo {public mut prop a: Int64 { // okget() {0}set(v) {}}mut prop b: Int64 {public get() { // error0}public set(v) {} // error}}
属性可以使用访问控制修饰符有
private,protected,publicclass Foo {private prop a: Int64 { // okget() { 0 }}protected prop b: Int64 { // okget() { 0 }}public static prop c: Int64 { // okget() { 0 }}}
实例属性像实例函数一样, 可以使用
open和override修饰使用
open修饰的属性, 子类型可以使用override覆盖父类型的实现(override是可选的)open class A {public open mut prop i: Int64 {get() { 0 }set(v) {}}}class B <: A {override mut prop i: Int64 {get() { 1 }set(v) {}}}
静态属性像静态函数一样, 可以使用
redef修饰(redef是可选的), 子类型可以重新实现父类型的静态属性open class A {static mut prop i: Int64 {get() { 0 }set(v) {}}}class B <: A {redef static mut prop i: Int64 {get() { 1 }set(v) {}}}
子类型
override/redef使用let声明的实例属性必须要重新实现getter子类型
override/redef父类型中使用mut修饰符声明的的属性时, 允许只重新实现getter或setter, 但不能均不重新实现open class A {public open mut prop i1: Int64 {get() { 0 }set(v) {}}static mut prop i2: Int64 {get() { 0 }set(v) {}}}// case 1class B <: A {public override mut prop i1: Int64 {get() { 1 } // ok}redef static mut prop i2: Int64 {get() { 1 } // ok}}// case 2class B <: A {public override mut prop i1: Int64 {set(v) {} // ok}redef static mut prop i2: Int64 {set(v) {} // ok}}// case 3class B <: A {override mut prop i1: Int64 {} // errorredef static mut prop i2: Int64 {} // error}
子类型的属性
override/redef必须与父类保持相同的mut修饰符, 并且还必须保持相同的类型class P {}class S {}open class A {open prop i1: P {get() { P() }}static prop i2: P {get() { P() }}}// case 1class B <: A {override mut prop i1: P { // errorset(v) {}}redef static mut prop i2: P { // errorset(v) {}}}// case 2class B <: A {override prop i1: S { // errorget() { S() }}redef static prop i2: S { // errorget() { S() }}}
我不理解这一句话: 子类型override/redef使用let声明的实例属性必须要重新实现getter
-
属性并不能被
let和var修饰 -
经测试, 就算父类型属性基于
let成员变量, 如果不显式覆盖, 也可以直接不重新实现属性 -
假设要显式覆盖属性, 就必须要重新实现
getter, 那跟let或var也没有任何关系
所以我不是很理解, 这一句话是什么意思?
子类
override父类的属性时, 可以使用super调用父类的实例属性open class A {open prop v: Int64 {get() { 1 }}}class B <: A {override prop v: Int64 {get() { super.v + 1 }}}
其实不仅是通过super访问父类的实例属性, 还能访问其他成员