类(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);5alias 与 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 家电 5new 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 2static 成员挂在类型上,经 类型名.成员 访问。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 0b[i] += 5这类复合赋值脱糖成读 + 写两步,读写都经由声明的索引器。- 多元下标照常:
operator [](self, x: int, y: int) -> int配g[1, 2]使用。 - 索引器参数不支持
ref/out。
