华为仓颉语言的class(类)初步
华为仓颉语言的class是面向对象编程的核心,用于定义引用类型对象。
华为仓颉语言的class(类)初步
class 概念
class 是仓颉面向对象体系的核心,用来描述“引用类型”对象。与 struct(值类型)相比,class 实例在赋值、传参时是引用语义,并天然支持继承与多态 。注意:class 只能定义在源文件的顶层作用域。
class 类型的定义以关键字 class 开头,后跟 class 的名字,接着是定义在一对花括号中的 class 定义体。class 定义体中可以定义一系列的成员变量(member variables)、成员属性(member properties)、静态初始化器(static initializers)、构造函数(constructors)、成员函数(member functions)和操作符函数(operator functions)。
一个类的基本定义语法如下:
class 类名 {
// 成员变量
// 构造函数
// 成员函数
// 成员属性
}
说明:
类名通常推荐采用帕斯卡命名法(首字母大写,如 Person、Car)。
类体中可包含成员变量、成员方法、构造函数、成员属性等,用于描述类的特征数据和行为。
成员属性:提供了对成员变量更灵活和安全的访问方式,可以包含自定义的 getter 和 setter。
【关于Property(属性)的说明
在仓颉语言(Cangjie)的官方文档中,统一使用 Property 来表示“属性”。
属性(Properties)提供了一个 getter 和一个可选的 setter 来间接获取和设置值。
仓颉语言的class 定义体中
数据成员变量不像有些语言那样称为attribute。
成员属性就是指属性(Properties)。
|
术语 |
英文 |
定义 |
|
属性 |
Property |
通过getter/setter间接访问的成员,提供计算、验证等逻辑 |
|
成员变量 |
Member Variable |
直接存储数据的普通字段(无getter/setter) |
|
attribute |
❌ 非官方术语 |
在仓颉中不适用(其他语言如Python的术语) |
】
例如:
class Rectangle {
// 成员变量
let width: Int64
let height: Int64
// 构造函数
public init(width: Int64, height: Int64) {
this.width = width
this.height = height
}
// 成员函数
public func area(): Int64 {
return width * height
}
}
创建对象
语法
let/var 变量名[: 类型] = 类型名(构造函数参数)
通常[: 类型]部分可省略,由编译器自动推断
示例:简单类的定义及对象创建示例
// 定义一个简单的 Person 类
class Person {
var name: String
var age: Int64
// 构造函数
public init(name: String, age: Int64) {
this.name = name
this.age = age
}
public func introduce(): Unit {
println("Hello, my name is ${name} and I am ${age} years old.")
}
}
// 在 main 函数或其他方法中创建对象
main() {
// 1. 使用 var 声明,可以改变引用指向的对象
var person1 = Person("Alice", 30) // 调用 init(name, age) , 等号前也可写为var person1: Person =
person1.introduce() // 输出: Hello, my name is Alice and I am 30 years old.
// person1 引用可以重新赋值,指向一个新的 Person 对象
person1 = Person("Bob", 25)
person1.introduce() // 输出: Hello, my name is Bob and I am 25 years old.
// 2. 使用 let 声明,引用不可变
let person2 = Person("Charlie", 40)
person2.introduce() // 输出: Hello, my name is Charlie and I am 40 years old.
// 下面这行代码会编译错误,因为 person2 是不可变引用
// person2 = Person("David", 35)
// 但是,对象内部的成员变量如果是 var,仍然可以通过引用修改
person2.age = 41 // 这是允许的,因为 person2.age 是 var
person2.introduce() // 输出: Hello, my name is Charlie and I am 41 years old.
}
编译运行截图:

仓颉class 几点说明
• 成员变量写在类体;可追加 public / private / protected 等可见性修饰符(缺省为 internal)。
【访问控制修饰符:
private 私有,表示在 class 定义内可见。
internal 内部,表示仅当前包及子包(包括子包的子包,详见包)内可见。缺省。
protected 受保护,表示当前模块(详见包)及当前类的子类可见。
public 公开,表示模块内外均可见。】
• 仓颉class 支持两类构造函数,规则与初始化顺序全部与 struct 一致 :
普通构造函数:名字固定为 init,可重载。
主构造函数:名字与类名相同,最多一个,可把形参同时声明为成员变量。
一个 类可以有多个构造函数。
例如:
class Person {
var name: String
var age: Int64
// 主构造函数
public init(name: String, age: Int64) {
this.name = name
this.age = age
}
// 辅助构造函数,调用主构造
public init(name: String) {
this.init(name, 0) // 调用主构造函数
}
}
初始化顺序:
① 先初始化主构造之外、带缺省值的变量;
② 如果构造函数体内未显式调用父类构造函数或本类其他构造函数,则调用父类的无参构造函数 super(),如果父类没有无参构造函数,则报错;
③ 执行构造函数体。
示例
open class Parent {
var a: Int64 = 1 // 步骤1.1:父类字段初始化
var b: Int64
// 父类主构造函数
public init(b: Int64) {
// 步骤3.1:执行父类构造函数体
this.b = b
println("Parent constructor body")
}
}
class Child <: Parent {
var x: Int64 = 3 // 步骤1.2:子类字段初始化
var y: Int64
// 子类主构造函数
public init(b: Int64, y: Int64) {
// 隐式或显式调用父类构造函数!这是步骤2
super(b) // 步骤2:调用父类构造函数
// 步骤3.2:执行子类构造函数体
this.y = y
println("Child constructor body")
}
}
main() {
// 创建实例时:
let obj = Child(2, 4)
}
输出:
Parent constructor body
Child constructor body
发生的顺序实际上是:
1.Parent.a 被赋值为 1
2.Child.x 被赋值为 3
3.开始执行 Child.init 中的代码:
a. 首先调用 super(b),即 Parent.init(b)。
b. 执行 Parent.init:
- Parent.b 被赋值为 2 (来自参数)。
- 打印 "Parent constructor body"。
c. Parent.init 执行完毕,返回。
4.继续执行 Child.init 的剩余部分:
a. Child.y 被赋值为 4。
b. 打印 "Child constructor body"。
示例:父类没有无参构造,子类忘记显式 super → 编译错误
open class Parent {
var b: Int64
public init(b: Int64) {
this.b = b
println("Parent body")
} // 只有这一个构造函数
}
class Child <: Parent {
public init() {
super(0) // 注意:若无此行,编译报错!
println("Child body")
}
}
main() {
// 创建实例时:
let obj = Child()
}
属性(Property)
仓颉的属性(Property)提供了对类内部状态的受控访问,它可以包含 getter 和 setter。
示例:
class Person {
private var _age: Int64 = 0
// 1) 可读写属性:带 mut,可在 set 中拦截或记录
public mut prop age: Int64 {
get() {
println("getter: 读取 _age")
_age
}
set(value) {
println("setter: 写入 _age = ${value}")
_age = value
}
}
// 2) 只读计算属性:没有 setter,始终根据 _age 计算
public prop isAdult: Bool {
get() {
_age >= 18
}
}
}
// ---------------- 演示 ----------------
main(): Int64 {
let p = Person()
println("--- 写属性 ---")
p.age = 15 // 触发 setter
println("--- 读属性 ---")
let a = p.age // 触发 getter
println("读取到的 age = ${a}")
println("--- 只读属性 ---")
println("isAdult = ${p.isAdult}") // 15 < 18 → false
p.age = 20 // 再次触发 setter
println("isAdult = ${p.isAdult}") // 20 ≥ 18 → true
0 // return 0
}
输出:
--- 写属性 ---
setter: 写入 _age = 15
--- 读属性 ---
getter: 读取 _age
读取到的 age = 15
--- 只读属性 ---
isAdult = false
setter: 写入 _age = 20
isAdult = true
抽象类(abstract class)
抽象类使用 abstract 修饰,不能实例化的类,用于定义子类的共同接口,可包含抽象方法(无实现)和具体方法(有实现),子类必须实现抽象方法。
抽象类定义时的 open 修饰符是可选的——抽象类默认可继承(无需open)。
注意:
• 抽象类中禁止定义 private 的抽象函数;
• 不能为抽象类创建实例;
• 抽象类的非抽象子类必须实现父类中的所有抽象函数。
抽象类还可以被 sealed 修饰,表示该抽象类只能在其定义所在的包内被其他类继承。
示例1:
// 1. 抽象类定义,本身就隐含 open,无需再写
abstract class Shape {
// 抽象方法:
public func getArea(): Float64
// 具体方法:实现写在方法体里
public func printArea(): Unit {
println("面积:${this.getArea()}")
}
}
// 2. 子类
class Circle <: Shape {
let radius: Float64 // 成员变量用 let/var 声明
// 构造函数:仓颉只有 init,没有 constructor
public init(radius: Float64) {
super() // 调用父类构造函数
this.radius = radius
}
// 实现抽象方法
public override func getArea(): Float64 {
3.1415926 * radius * radius
}
}
// 3. 创建对象
main() {
let circle: Shape = Circle(2.0)
circle.printArea() // 输出:面积:12.5663704
}
示例 2:
// 1. 抽象类定义,本身就隐含 open,无需再写
abstract class AbstractBase {
// 抽象方法:
public func doSomething(): Unit
}
// 2. 同包下的实现类
class Concrete <: AbstractBase {
// 实现抽象方法
public override func doSomething(): Unit {
println("实现抽象方法")
}
}
// 3. 密封抽象类:仅本包可继承
sealed abstract class SealedAbstract {
public func baseFunc(): Unit {} // 具体方法
}
// 4. 同包子类继承密封抽象类——合法
class SamePackageChild <: SealedAbstract {}
// 6. 演示 main
main() {
let obj1: AbstractBase = Concrete()
obj1.doSomething() // 输出:实现抽象方法
let obj2 = SamePackageChild()
obj2.baseFunc() // 无输出,仅演示可实例化
}
class是引用类型
华为仓颉语言的class是引用类型,定义为引用类型的变量,变量名中存储的是指向数据值的引用,因此在进行赋值或函数传参等操作时,拷贝的是引用。
示例
class MyClass {
var value: Int64 = 0
}
main(): Int64 {
let a = MyClass() // a 是一个引用,指向新创建的 MyClass 对象
a.value = 10
let b = a // 将 a 的引用拷贝给 b。现在 a 和 b 指向同一个对象!
b.value = 20 // 通过 b 修改对象
println(a.value) // 输出 20,因为 a 指向的对象已被修改
return 0
}
继承 / 实现 / 多态要点
• 单继承:用 <: 符号,如 class Student <: Person { ... } 。
• 父类必须被 open 修饰才能被继承。
• 方法重写:子类方法前加 override;如果一个方法没有 open 修饰,则子类不能重写它。
【注意,“方法重写(override)”只是实现运行时多态的一种技术手段;多态(Polymorphism)本身是一个更宽泛的概念。】
示例:
// 1. 父类定义:// 类必须用 open 才能被继承,默认实现返回 0
open class Shape {
public open func area(): Float64 { // 用 open 声明可重写
0.0
}
func show() { // 非 open,子类不能重写
println("面积 = ${this.area()}")
}
}
// 2. 子类 Circle:重写 area()
class Circle <: Shape {
let r: Float64
init(r: Float64) {
super()
this.r = r
}
public override func area(): Float64 { // 必须写 override
3.1415926 * r * r
}
}
// 3. 子类 Rectangle:再次重写 area()
class Rectangle <: Shape {
let w: Float64
let h: Float64
init(w: Float64, h: Float64) {
super()
this.w = w
this.h = h
}
public override func area(): Float64 {
w * h
}
}
// 4. 演示多态
main(): Int64 {
let shapes: Array<Shape> = [Circle(2.0), Rectangle(3.0, 4.0)]
for (s in shapes) {
s.show() // 运行时根据实际对象调用对应 override
}
0 return 0
}
输出:
面积 = 12.566370
面积 = 12.000000
更多推荐



所有评论(0)