Skip to Content

类(Class)

class 是引用语义的复合类型:赋值与传参共享同一对象,改别名就是改本体。类支持单继承、虚方法、抽象与静态成员,是承载行为与身份的数据的主要声明形态。

声明与构造

class Animal { public var name: string = "?"; init(n: string) { name = n; } public virtual func Speak() -> string => "..."; } class Dog : Animal { public var tricks: int = 0; init(n: string) : base(n) { } public override func Speak() -> string => "汪"; }

构造器写 init,经 new 触发,不能直接调用。: base(n) 链到基类构造器,省略链等价 : base()。virtual 声明可被覆写的虚方法,override 在派生类提供新实现,派发按运行时类型。

引用语义

val d = new Dog("旺财"); val alias = d; alias.tricks = 5; print(d.tricks);
5

alias 与 d 是同一个对象。这与结构体的值拷贝形成对照:需要独立副本时,类要自己提供 Clone 一类的方法(见协议的 Self 示例)。

构造器委托与 base

构造器之间用 : init(...) 委托同类重载,用 : base(...) 链到基类,省略链等价 : base():

class Widget { public val serial: int; public var label: string = "标准"; init(serial: int, label: string) { self.serial = serial; self.label = label; } init(serial: int) : init(serial, "标准") { } } class Gadget : Widget { public var watts: int = 0; init(s: int) : base(s, "家电") { watts = 5; } } val w = new Widget(1); print(w.serial, w.label); val g = new Gadget(2); print(g.serial, g.label, g.watts);
1 标准 2 家电 5

new Widget(1) 走委托链补上默认标签,Gadget 的 init 经 base 把标签定为「家电」。

主构造器与重载集

主构造器(class Name(args) { })把参数提升为 private val 字段,并并入构造器重载集:实参元数命中显式 init 就走 init,否则命中主构造器的参数形状,则走主构造的路径。当显式 init 和主构造参数形状相同时,显式 init 胜出:

class Pack(n: int) { public var shown: string = "未设"; init(n: int) { shown = "显式"; } } class Crate(n: int) { public var shown: string = "主构"; init() { shown = "零参"; } } print(new Pack(1).shown); print(new Crate(7).shown); print(new Crate().shown);
显式 主构 零参

主构造器暂时没有链式语法位,省链按「隐式 : base()」的规则处理。

声明了显式构造器的类型还有一条定值契约:无初始化器的 val 实例字段必须在每个构造器(含 : init(...) 委托链)的赋值并集内被赋值,否则编译期报错。而 var 字段零值豁免,无显式构造器的类型(走对象初始化器路径)豁免。

可失败构造

init 带 fail 子句时,new 表达式本身可失败,调用点必须消费,try? 接住后失败产 null:

error BadCode(code: int); class Vault { public val code: int; init(code: int) fail BadCode { if code < 0 { fail new BadCode(code); } self.code = code; } } val v = try? new Vault(9); print(v?.code); val bad = try? new Vault(-1); print(bad == null);
9 True

失败沿构造与委托链逐层透出。字段初始化器里禁止调用可失败构造。

多态

基类引用装派生对象,虚方法按实际类型派发:

val zoo = new list<Animal>(); zoo.Add(new Animal("_GENERIC_")); zoo.Add(new Dog("旺财")); for pet in zoo { print(pet.Speak()); }
... 汪

遮蔽与覆写

基类方法没标 virtual 时,派生类的同名方法是遮蔽不是覆写:

class Plain { public func Go1() -> string => "基"; public virtual func Go2() -> string => "基"; } class Shadowed : Plain { public func Go1() -> string => "派"; public override func Go2() -> string => "派"; } val d: Plain = new Shadowed(); print(d.Go1()); print(d.Go2());
基 派

要多态就把基类方法标 virtual、派生标 override。确实想遮蔽(提供无关的同名方法)就按警告提示改名,或确认忽略。

抽象与密封

abstract class Shape { public abstract func Area() -> float; } class Square : Shape { public var side: float = 1.0; public override func Area() -> float => side * side; }

abstract 类不可实例化,抽象成员没有方法体,派生类必须 override。sealed class 则禁止再被继承。

静态成员

class Counter { public static var total: int = 0; public var id: int = 0; init() { Counter.total += 1; id = Counter.total; } } val c1 = new Counter(); val c2 = new Counter(); print(c1.id, c2.id, Counter.total);
1 2 2

static 成员挂在类型上,经 类型名.成员 访问。static class 是静态工具类的专用形态:体内成员强制 static,并隐式 sealed,无法被继承。静态字段的初始化器进全局初始化。

自定义索引器

operator [] 声明下标读、operator []= 声明下标写,下标个数任意(矩阵 m[i, j] 场景):

class Box3 { val cells: list<int> = new list<int>(); init() { for i in 0..3 { cells.Add(0); } } public operator [](self, i: int) -> int => cells[i]; public operator []=(self, i: int, v: int) { cells[i] = v; } } func main() -> int { val b = new Box3(); b[1] = 9; b[1] += 5; print(b[1], b[0]); return 0; }
14 0
  • b[i] += 5 这类复合赋值脱糖成读 + 写两步,读写都经由声明的索引器。
  • 多元下标照常:operator [](self, x: int, y: int) -> int 配 g[1, 2] 使用。
  • 索引器参数不支持 ref / out。

注意

  • 无体分号形 class Marker; 等价空类体;
  • init / deinit 标 virtual、override、abstract 报 MS4025:构造与析构不参与虚派发。
  • 脚本版 MushScript 的析构器 deinit() 只在 using 退出时触发,普通块退出不做承诺。
  • 主构造器把参数提升为 private val 字段,并与显式 init 同入重载集,见上文「主构造器与重载集」。
  • 类实现协议用 impl,与继承的 : 分属两个子句,见协议。
最后更新于 2026年10月9日