# 类 ## 功能 类用于把一组字段和方法封装成同一种对象结构。字段保存实例状态,方法描述实例行为,方法内部使用 `this` 访问当前实例。 BT 的类语法保持 JS 风格,但构造实例使用 `类名::构造方法(...)`。最常用的构造方法名是 `new`,所以通常写成 `User::new(...)`。 ## 语法 ### 定义类 ```text class 类名 { 字段名: 初始值 字段名 pub 公共字段: 初始值 方法名(参数1, 参数2) { 方法体 } pub 公共方法名(参数) { 方法体 } new(参数) { 初始化逻辑 this } } ``` ### 创建实例 ```text 对象 = 类名::new(参数) 对象 = 类名::其他公共构造方法(参数) ``` `Class::new()` 是 BT 当前的实例化写法,不是 `new Class()`。 ## 成员 类成员分为字段和方法。 字段使用 `名称: 表达式` 声明: ```bt class User { name: '未命名' age: 0 } ``` ### 裸字段 字段也可以只写名称,不写 `:` 和初始值,这种写法叫裸字段。裸字段会提前在实例里占一个成员名,默认值是 `empty`。 ```bt class User { name new(name) { this.name = name this } pub get_name() { this.name } } user = User::new('Tom') println user.get_name() ``` 裸字段 `name` 和 `name: ''` 不一样:`name` 的默认值是 `empty`,`name: ''` 的默认值是空字符串。构造方法里通常用 `this.name = name` 覆盖默认值。 方法不需要写 `fn`,直接写 `方法名(参数) { ... }`: ```bt class User { name: 'BT' pub get_name() { this.name } } ``` 方法和普通函数一样有返回值。没有显式 `return` 时,方法体最后一条语句的值就是返回值。 ## this `this` 表示当前正在操作的实例,只在类方法和构造方法里使用。 ```bt class Counter { value: 0 pub inc() { this.value++ this } pub get() { this.value } } counter = Counter::new() counter.inc().inc() println counter.get() ``` `this.value` 用来读取当前实例字段,`this.value = 1` 用来写入当前实例字段,`this.method()` 用来调用当前实例的其他方法。 访问私有成员时必须直接通过 `this.xxx`。不要把 `this` 先赋给别的变量再访问私有成员,因为权限判断只把直接来自 `this` 的访问视为类内部访问。 ```bt class User { password: '' new(password) { this.password = password this } check(password) { this.password == password } pub login(password) { this.check(password) } } ``` ## new `new` 是最常用的构造方法名。创建实例时写 `类名::new(...)`。 ```bt class User { name: '' age: 0 new(name, age) { this.name = name this.age = age this } pub info() { this.name + ':' + this.age } } user = User::new('Tom', 18) println user.info() ``` 如果类里没有定义 `new`,`Class::new()` 仍然会创建一个实例,并使用类字段里的默认值: ```bt class Config { pub host: '127.0.0.1' pub port: 8080 } config = Config::new() println config.host println config.port ``` 构造方法返回规则需要特别注意:BT 方法默认返回最后一条语句的值。构造方法执行后,如果返回 `empty` 或 `null`,`Class::new()` 会返回新实例;如果返回其他值,就会返回那个值。 因此构造方法里最后一行建议写 `this`,明确返回当前实例: ```bt class User { name: '' new(name) { this.name = name this } } ``` 不要只把赋值语句放在构造方法最后一行: ```bt class User { name: '' new(name) { this.name = name } } user = User::new('Tom') ``` 上面这个 `new` 的最后一条语句是赋值表达式,赋值表达式返回右侧的 `'Tom'`,所以 `User::new('Tom')` 返回的是字符串,不是实例。 ## 多个构造入口 BT 的构造入口不固定为 `new`。任何公开方法都可以用 `类名::方法名(...)` 作为构造入口:运行时会先创建实例,再把 `this` 绑定到这个实例执行指定方法。 ```bt class User { name: '' role: 'user' new(name) { this.name = name this } pub admin(name) { this.name = name this.role = 'admin' this } pub label() { this.name + ':' + this.role } } tom = User::new('Tom') root = User::admin('Root') println tom.label() println root.label() ``` 除 `new` 以外,作为构造入口的方法需要是 `pub`,否则类外不能访问。 ## 公共和私有 类成员默认是私有成员。只有加了 `pub` 的字段或方法,才能在类外部通过实例访问。 ```bt class Account { balance: 0 new(balance) { this.balance = balance this } pub deposit(amount) { this.balance += amount this } pub get_balance() { this.balance } can_withdraw(amount) { this.balance >= amount } pub withdraw(amount) { if this.can_withdraw(amount) { this.balance -= amount true } else { false } } } account = Account::new(100) account.deposit(50) println account.get_balance() println account.withdraw(80) println account.get_balance() ``` 外部可以访问: ```bt account.deposit(50) account.get_balance() ``` 外部不能访问: ```bt account.balance account.can_withdraw(10) ``` 私有成员仍然可以在类内部通过 `this` 访问: ```bt this.balance this.can_withdraw(10) ``` BT 当前没有 `private` 关键字。默认就是私有,需要公开时才写 `pub`。 ## 字段写入 公共字段可以在类外部读写: ```bt class User { pub name: '' } user = User::new() user.name = 'Tom' println user.name ``` 私有字段只能在类内部通过 `this` 写入: ```bt class User { name: '' pub set_name(name) { this.name = name this } pub get_name() { this.name } } ``` 如果在 `this` 上新增一个类定义里没有声明过的字段,这个字段会作为私有字段保存;如果在外部实例上新增字段,这个字段会作为公共字段保存。 ```bt class User { pub set_token(token) { this.token = token this } } user = User::new() user.set_token('abc') user.nickname = 'Tom' ``` 上面的 `token` 是类内部通过 `this` 新增的私有字段,`nickname` 是外部新增的公共字段。 ## 链式调用 BT 支持链式调用。类方法如果要继续链式调用,返回 `this` 即可。 ```bt class Query { table: '' where_sql: '' limit_size: 0 new(table) { this.table = table this } pub where(sql) { this.where_sql = sql this } pub limit(size) { this.limit_size = size this } pub sql() { 'select * from ' + this.table + ' where ' + this.where_sql + ' limit ' + this.limit_size } } sql = Query::new('users') .where('age >= 18') .limit(10) .sql() println sql ``` ## 参数 类本身没有调用参数。参数写在构造方法或普通方法上。 ```bt class Page { page: 1 size: 20 new(page = 1, size = 20) { this.page = page this.size = size this } } p1 = Page::new() p2 = Page::new(2, 50) ``` ## 返回值 - `class Name { ... }` 会创建一个类值,并把它保存到类名变量中。 - `Name::new(...)` 默认返回类实例。 - `Name::method(...)` 会先创建类实例,再执行指定方法作为构造入口。 - 实例方法返回方法体最后一条语句的值,也可以使用 `return` 强制返回。 - 构造方法返回 `empty` 或 `null` 时,构造调用返回新实例;返回其他值时,构造调用返回该值。 ## 代码示例 ### 完整封装示例 ```bt class User { name: '' password: '' pub role: 'user' new(name, password) { this.name = name this.password = password this } pub admin(name, password) { this.name = name this.password = password this.role = 'admin' this } pub get_name() { this.name } pub rename(name) { this.name = name this } check_password(password) { this.password == password } pub login(password) { this.check_password(password) } pub label() { this.name + '[' + this.role + ']' } } user = User::new('Tom', '123456') admin = User::admin('Root', 'root_pwd') println user.rename('Tommy').label() println user.login('123456') println admin.label() ``` ### 私有成员保护状态 ```bt class Counter { value: 0 new(start = 0) { this.value = start this } pub inc() { this.value++ this } pub dec() { this.value-- this } pub get() { this.value } } counter = Counter::new(10) counter.inc().inc().dec() println counter.get() ``` ## 注意事项 - `Class::new()` 是实例化语法;不要写 `new Class()`。 - `new` 是推荐的构造方法名;如果没有定义 `new`,`Class::new()` 也会返回带默认字段的实例。 - 构造方法最后一行建议写 `this`,避免最后一条赋值语句把字符串、数字等普通值当作构造结果返回。 - 成员默认私有;公开字段或方法必须写 `pub`。 - 私有成员只能通过 `this.xxx` 在类内部访问。 - 当前没有 `private`、`extends`、`super`、`static` 语法;不要按 JS 类继承写法使用。 - 字段初始值适合放标量默认值;数组、对象等可变值建议在 `new` 中用 `this.xxx = []` 或 `this.xxx = {}` 初始化,避免多个实例共享同一个默认引用。