Day 09 - Class 基础

目标:掌握ArkTS类的定义、访问修饰符、Getter/Setter、静态成员
预计时间:1.5-2小时
前置知识:Day 01-05 基础语法、Day 06-08 函数


第一部分:类的定义(对标 C++ class/struct)

1.1 为什么需要类

问题引入:假设你要管理一组传感器数据,每个传感器有ID、名称、当前读数。不用类的话怎么写?

// 不用类:数据分散,容易出错
let sensorId: number = 1001;
let sensorName: string = "温度传感器-A1";
let sensorValue: number = 25.5;

let sensor2Id: number = 1002;
let sensor2Name: string = "湿度传感器-B2";
let sensor2Value: number = 60.0;

// 操作数据的函数也要分别处理
function updateSensorValue(value: number): void {
  sensorValue = value; // 只能操作全局变量
}

问题

  • 变量名容易冲突(sensorId vs sensor2Id
  • 数据和操作分离,维护困难
  • 无法创建多个结构相同的对象

解决方案:将数据和行为封装在一起 →


1.2 基本类定义

class Sensor {
  // 属性声明:必须显式声明类型和初始值
  id: number;
  name: string;
  currentValue: number;

  // 构造函数
  constructor(id: number, name: string, initialValue: number) {
    this.id = id;
    this.name = name;
    this.currentValue = initialValue;
  }

  // 成员方法
  updateValue(newValue: number): void {
    this.currentValue = newValue;
  }

  getStatus(): string {
    return `${this.name}: ${this.currentValue}`;
  }
}

// 创建实例
let tempSensor: Sensor = new Sensor(1001, "温度传感器", 25.5);
let humiSensor: Sensor = new Sensor(1002, "湿度传感器", 60.0);

// 调用方法
tempSensor.updateValue(26.0);
console.log(`结果:${tempSensor.getStatus()}`); // "温度传感器: 26"

对比C++

class Sensor {
public:
    int id;
    std::string name;
    double currentValue;
    
    Sensor(int id, const std::string& name, double initialValue)
        : id(id), name(name), currentValue(initialValue) {}
    
    void updateValue(double newValue) {
        currentValue = newValue;
    }
    
    std::string getStatus() const {
        return name + ": " + std::to_string(currentValue);
    }
};

// 创建实例
Sensor tempSensor(1001, "温度传感器", 25.5);
tempSensor.updateValue(26.0);

关键差异

  • C++ 用 ClassName(params) 构造,ArkTS 用 new ClassName(params)
  • C++ 用初始化列表 : id(id), name(name),ArkTS 在构造函数内用 this.id = id

1.3 属性必须有初始值

ArkTS严格要求:类属性必须有初始值,或在构造函数中赋值。

class Device {
  // ✅ 方式1:声明时初始化
  id: number = 0;
  name: string = "";

  // ✅ 方式2:在构造函数中赋值
  serialNumber: string;

  constructor(serial: string) {
    this.serialNumber = serial; // 必须在这里赋值
  }
}

// ❌ 错误:属性没有初始值,也没有在构造函数中赋值
class BadDevice {
  id: number; // 错误!没有初始值
  name: string; // 错误!没有初始值
  // constructor 缺失或没有给属性赋值
}

对比C++

  • C++ 成员可以在构造函数初始化列表中赋值
  • C++ 基本类型不初始化会有垃圾值,但不会编译错误
  • ArkTS 强制要求明确初始化,更安全

第二部分:访问修饰符(对标 C++ public/private/protected)

2.1 public —— 默认就是public

重要区别:ArkTS 类成员默认就是 public,这与 C++ 默认 private 正好相反!

class Device {
  // 这三行等价,都声明为 public
  id: number;
  public name: string;
  public status: string;

  constructor(id: number, name: string) {
    this.id = id;
    this.name = name;
    this.status = "offline";
  }

  // 方法默认也是 public
  turnOn(): void {
    this.status = "online";
  }
}

let dev: Device = new Device(1001, "灯-001");
console.log(`结果:${dev.id}`); // ✅ 可以访问
console.log(`结果:${dev.name}`); // ✅ 可以访问
dev.turnOn(); // ✅ 可以调用

对比C++

class Device {
    // C++ 默认是 private!
    int id;              // private
    std::string name;    // private
    
public:
    // 必须显式声明 public 才能外部访问
    void turnOn() { /* ... */ }
};

Device dev(1001, "灯-001");
// dev.id;     // ❌ 编译错误,id 是 private

记忆口诀:ArkTS 默认"开放",C++ 默认"封闭"。


2.2 private —— 私有成员

class SecureDevice {
  public id: number;
  public name: string;

  // private 成员:只能在类内部访问
  private _password: string;
  private _isLocked: boolean = true;

  constructor(id: number, name: string, password: string) {
    this.id = id;
    this.name = name;
    this._password = password;
  }

  // public 方法可以访问 private 成员
  public unlock(password: string): boolean {
    if (password === this._password) {
      this._isLocked = false;
      return true;
    }
    return false;
  }

  public operate(): string {
    if (this._isLocked) {
      return "设备已锁定,无法操作";
    }
    return "执行操作...";
  }

  // private 方法:内部辅助逻辑
  private _logAccess(): void {
    console.log(`结果:${this.name} 被访问`);
  }
}

let device: SecureDevice = new SecureDevice(1001, "安全设备", "123456");
console.log(`结果:${device.id}`); // ✅ OK
// console.log(device._password);          // ❌ 错误!private
// console.log(device._isLocked);          // ❌ 错误!private
// device._logAccess();                    // ❌ 错误!private

device.unlock("123456");
console.log(`结果:${device.operate()}`); // "执行操作..."

命名惯例:private 成员通常用 _ 前缀(如 _password),便于区分。

对比C++:用法完全相同,只是 ArkTS 需要显式写 private


2.3 protected —— 子类可访问

class BaseDevice {
  public id: number;
  private _secret: string = "secret";

  // protected:类内部和子类可以访问,外部不能
  protected firmwareVersion: string = "1.0.0";

  protected _updateFirmware(version: string): void {
    this.firmwareVersion = version;
  }
}

// 子类(Day 10 会详细讲继承)
class SmartLight extends BaseDevice {
  updateToV2(): void {
    // ✅ 子类可以访问 protected 成员
    this._updateFirmware("2.0.0");
    console.log(`结果:固件更新到 ${this.firmwareVersion}`);
  }
}

let light: SmartLight = new SmartLight();
light.updateToV2(); // ✅ OK
// light._updateFirmware("3.0");  // ❌ 错误!外部不能访问 protected
// light.firmwareVersion;          // ❌ 错误!外部不能访问 protected

预告extends 继承语法将在 Day 10 详细讲解,这里只需理解 protected 的访问范围。


第三部分:属性进阶

3.1 readonly 属性 —— 只读属性

问题引入:设备的ID一旦创建就不应该改变,怎么限制?

class DeviceConfig {
  readonly id: number; // 只读属性
  readonly createdAt: number; // 只读属性
  name: string;

  constructor(id: number, name: string) {
    this.id = id; // ✅ 构造函数中可以赋值
    this.createdAt = Date.now(); // ✅ 构造函数中可以赋值
    this.name = name;
  }

  updateName(newName: string): void {
    this.name = newName; // ✅ OK
    // this.id = 999;                // ❌ 错误!readonly 属性不能修改
  }
}

let config: DeviceConfig = new DeviceConfig(1001, "配置-001");
console.log(`结果:ID=${config.id}, 创建时间=${config.createdAt}`);
// config.id = 1002;  // ❌ 编译错误

对比C++

class DeviceConfig {
public:
    const int id;           // const 成员 = readonly
    const long long createdAt;
    std::string name;
    
    DeviceConfig(int id, const std::string& name)
        : id(id), createdAt(std::time(nullptr)), name(name) {}
};

共同点:都只能在构造函数中初始化,之后不能修改。


第四部分:Getter / Setter(对标 C++ 手写getter/setter,但语法更优雅)

4.1 基本语法

C++ 风格:手动写 get/set 方法。

class CppStyleDevice {
  private _brightness: number = 0;

  getBrightness(): number {
    return this._brightness;
  }

  setBrightness(value: number): void {
    this._brightness = value;
  }
}

let dev: CppStyleDevice = new CppStyleDevice();
dev.setBrightness(80);
console.log(`结果:${dev.getBrightness()}`);

ArkTS 风格:使用 get / set 关键字,像访问属性一样调用。

class DimmableLight {
  private _brightness: number = 0;

  // getter:像属性一样读取
  get brightness(): number {
    return this._brightness;
  }

  // setter:像属性一样赋值
  set brightness(value: number) {
    this._brightness = value;
  }
}

let light: DimmableLight = new DimmableLight();
light.brightness = 80; // 调用 setter
console.log(`结果:${light.brightness}`); // 调用 getter,输出 80

优势:使用方代码更简洁,像直接访问属性,但内部可以添加逻辑。


4.2 数据验证 —— 在setter中添加验证

class ValidatedLight {
  private _brightness: number = 0;
  private _isOn: boolean = false;

  get brightness(): number {
    return this._brightness;
  }

  // setter 中加入验证逻辑
  set brightness(value: number) {
    if (value < 0 || value > 100) {
      console.log("错误:亮度必须在0-100之间");
      return;
    }
    this._brightness = value;
    this._isOn = value > 0;
  }

  get isOn(): boolean {
    return this._isOn;
  }
}

let light: ValidatedLight = new ValidatedLight();
light.brightness = 80; // ✅ 正常设置
console.log(`结果:亮度=${light.brightness}, 状态=${light.isOn}`);

light.brightness = 150; // ❌ 输出错误信息,不会设置
console.log(`结果:亮度=${light.brightness}`); // 仍然是 80

4.3 计算属性 —— 在getter中返回计算值

class PowerDevice {
  private _brightness: number = 0;
  private readonly _maxPower: number = 50; // 最大功率50W

  get brightness(): number {
    return this._brightness;
  }

  set brightness(value: number) {
    this._brightness = Math.max(0, Math.min(100, value));
  }

  // 计算属性:根据亮度计算功耗
  get powerConsumption(): number {
    return (this._brightness / 100) * this._maxPower;
  }

  // 计算属性:根据亮度估算寿命剩余百分比
  get estimatedLifeRemaining(): number {
    // 假设满亮度寿命10000小时,每降低10%亮度寿命增加20%
    let factor: number = 1 + (100 - this._brightness) / 10 * 0.2;
    return 100 / factor;
  }
}

let device: PowerDevice = new PowerDevice();
device.brightness = 80;
console.log(`结果:功耗=${device.powerConsumption}W`); // 40W
console.log(`结果:寿命剩余=${device.estimatedLifeRemaining}%`); // 计算值

4.4 私有属性 + 公开访问器模式

这是封装的最佳实践:

class TemperatureSensor {
  private _celsius: number = 0;

  // 摄氏度读写
  get celsius(): number {
    return this._celsius;
  }

  set celsius(value: number) {
    if (value < -273.15) {
      console.log("错误:温度不能低于绝对零度");
      return;
    }
    this._celsius = value;
  }

  // 华氏度只读(计算属性)
  get fahrenheit(): number {
    return this._celsius * 9 / 5 + 32;
  }

  // 开尔文只读(计算属性)
  get kelvin(): number {
    return this._celsius + 273.15;
  }
}

let sensor: TemperatureSensor = new TemperatureSensor();
sensor.celsius = 25;
console.log(`结果:${sensor.celsius}°C = ${sensor.fahrenheit}°F = ${sensor.kelvin}K`);
// sensor.fahrenheit = 100;  // ❌ 错误!没有 setter

4.5 Getter/Setter 的局限性与最佳实践

局限性 1:属性与访问器不能同名

ArkTS 限制:类的属性(无论访问修饰符)和 getter/setter 不能使用相同的标识符名。

// ❌ 错误:Duplicate identifier 'port'
class Device {
  private port: number = 25; // 属性叫 port

  get port(): number {
    ...
  } // ❌ 重名!编译错误

  set port(value: number) {
    ...
  } // ❌ 重名!
}

// ✅ 正确:属性使用下划线前缀
class Device {
  private _port: number = 25; // 属性加下划线

  get port(): number { // getter 无下划线
    return this._port;
  }

  set port(value: number) { // setter 无下划线
    this._port = value;
  }
}

局限性 2:Setter 禁止返回类型注解

class Light {
  private _brightness: number = 0;

  // ❌ 错误:ArkTS 不允许写 :void
  set brightness(value: number): void {
    this._brightness = value;
  }

  // ✅ 正确:必须省略返回类型
  set brightness(value: number) {
    this._brightness = value;
  }
}

局限性 3:Setter 无法返回值

Setter 不能返回任何值,调用方无法直接知道设置是否成功:

class ValidatedLight {
  private _brightness: number = 0;

  set brightness(value: number) {
    if (value < 0 || value > 100) {
      console.log("错误:亮度超出范围");
      return; // 只能直接返回,不能返回 false
    }
    this._brightness = value;
  }
}

let light = new ValidatedLight();
light.brightness = 150; // 设置失败,但调用方无法知道

解决方案:如果需要知道操作结果,使用普通方法代替 setter:

class BetterLight {
  private _brightness: number = 0;

  // 用方法代替 setter,可以返回 boolean
  setBrightness(value: number): boolean {
    if (value < 0 || value > 100) {
      return false; // 返回失败
    }
    this._brightness = value;
    return true; // 返回成功
  }

  get brightness(): number {
    return this._brightness;
  }
}

let light = new BetterLight();
let ok = light.setBrightness(150);
if (!ok) {
  console.log("设置失败");
}

最佳实践建议

场景 推荐方案
简单属性读写,无需验证 直接暴露 public 属性
需要验证/计算,但不需要返回结果 使用 getter/setter
需要知道操作是否成功 使用普通方法,返回 boolean
只读计算属性 只提供 getter,不提供 setter

第五部分:Static 成员(对标 C++ static)

5.1 静态属性 —— 类级别共享数据

问题引入:如何统计创建了多少个设备实例?

class DeviceManager {
  // 静态属性:所有实例共享同一个变量
  static instanceCount: number = 0;
  static readonly MAX_DEVICES: number = 100;

  // 实例属性:每个实例独立
  id: number;
  name: string;

  constructor(name: string) {
    if (DeviceManager.instanceCount >= DeviceManager.MAX_DEVICES) {
      throw new Error("设备数量已达上限");
    }
    this.id = ++DeviceManager.instanceCount;
    this.name = name;
  }
}

let d1: DeviceManager = new DeviceManager("设备-A");
let d2: DeviceManager = new DeviceManager("设备-B");
let d3: DeviceManager = new DeviceManager("设备-C");

console.log(`结果:实例1 ID=${d1.id}`); // 1
console.log(`结果:实例2 ID=${d2.id}`); // 2
console.log(`结果:实例3 ID=${d3.id}`); // 3
console.log(`结果:总实例数=${DeviceManager.instanceCount}`); // 3
console.log(`结果:上限=${DeviceManager.MAX_DEVICES}`); // 100

对比C++

class DeviceManager {
public:
    static int instanceCount;
    static const int MAX_DEVICES = 100;
    
    DeviceManager(const std::string& name) {
        id = ++instanceCount;
        this->name = name;
    }
    
    int id;
    std::string name;
};

// C++ 必须在类外初始化静态成员
int DeviceManager::instanceCount = 0;

重要注意:静态属性如果不初始化,值为 undefined,不是默认值!

class Test {
  static num: number; // 未初始化,值是 undefined,不是 0!
  static str: string; // 未初始化,值是 undefined,不是 ""!
  static flag: boolean; // 未初始化,值是 undefined,不是 false!
}

console.log(Test.num); // undefined
console.log(Test.str); // undefined
console.log(Test.flag); // undefined

建议:静态属性始终显式初始化,避免意外。


5.2 静态方法 —— 通过类名调用

class MathUtils {
  // 静态方法:不依赖实例状态
  static clamp(value: number, min: number, max: number): number {
    return Math.max(min, Math.min(max, value));
  }

  static degToRad(degrees: number): number {
    return degrees * Math.PI / 180;
  }

  static radToDeg(radians: number): number {
    return radians * 180 / Math.PI;
  }
}

// 直接通过类名调用,不需要创建实例
let clamped: number = MathUtils.clamp(150, 0, 100);
console.log(`结果:限制在0-100之间:${clamped}`); // 100

let rad: number = MathUtils.degToRad(180);
console.log(`结果:180度 = ${rad}弧度`); // 3.14159...

5.3 实用场景 —— 计数器、工厂方法

class DeviceFactory {
  private static _serialCounter: number = 0;

  // 工厂方法:创建带自动序列号的设备
  static createDevice(name: string): DeviceInfo {
    DeviceFactory._serialCounter++;
    return new DeviceInfo(
      DeviceFactory._serialCounter,
      `${name}-${DeviceFactory._serialCounter}`
    );
  }

  static getTotalCreated(): number {
    return DeviceFactory._serialCounter;
  }
}

class DeviceInfo {
  constructor(
    public id: number,
    public name: string
  ) {
  }
}

// 使用工厂方法创建设备
let dev1: DeviceInfo = DeviceFactory.createDevice("传感器");
let dev2: DeviceInfo = DeviceFactory.createDevice("传感器");
let dev3: DeviceInfo = DeviceFactory.createDevice("执行器");

console.log(`结果:${dev1.name}`); // "传感器-1"
console.log(`结果:${dev2.name}`); // "传感器-2"
console.log(`结果:${dev3.name}`); // "执行器-3"
console.log(`结果:总计创建=${DeviceFactory.getTotalCreated()}`); // 3

第六部分:小结与练习

6.1 知识点对比总结表(ArkTS vs C++)

概念 C++ ArkTS 关键差异
类定义 class class 相同
构造函数 ClassName(params) constructor(params) 关键字不同
构造函数重载 ✅ 支持 ❌ 不支持 ArkTS 只能用默认参数模拟
属性初始化 初始化列表 构造函数内 this.x = x 语法不同
默认访问修饰符 private public 重要区别
public public: public(可省略) ArkTS默认就是public
private private: private 用法相同,但ArkTS一对一声明
protected protected: protected 用法相同
readonly/const const成员 readonly 关键字不同
参数属性简写 ❌ ArkTS不支持 TypeScript支持,ArkTS禁止
getter/setter 手动方法 get/set关键字 ArkTS语法更优雅但有局限
setter返回类型 任意 ❌ 禁止写返回类型 ArkTS特殊限制
属性与访问器重名 ✅ 允许 ❌ 禁止 ArkTS限制:Duplicate identifier
静态成员 static static ArkTS可在类内初始化
静态属性默认值 自动初始化 undefined ArkTS不初始化则为undefined

6.2 练习题

练习1:选择题

  1. ArkTS类成员默认的访问修饰符是?

    • A. private
    • B. public
    • C. protected
    • D. 没有默认值
  2. 以下哪个是ArkTS合法的readonly属性用法?

    • A. readonly x: number = 0; 声明时初始化
    • B. 在构造函数中赋值
    • C. 以上都是
    • D. 以上都不是
  3. ArkTS枚举的限制是?

    • A. 不能用于switch语句
    • B. 不能同时包含数字和字符串成员
    • C. 必须显式指定所有值
    • D. 最多只能有10个成员
练习1答案

答案:1.B 2.C 3.B

题号 答案 解析
1 B ArkTS 类成员默认就是 public,与 C++ 默认 private 相反
2 C readonly 属性可以在声明时初始化,也可以在构造函数中赋值,之后不能修改
3 B ArkTS 枚举禁止混合类型(不能同时有数字和字符串成员)

练习2:代码补全

补全以下类定义,实现一个带验证的邮箱配置类:

class EmailConfig {
  readonly id: number;
  address: string; // public 属性
  // 补全:smtpServer 是 private,字符串类型
  ______
  ______: ______;
  // 补全:_port 是 private,number类型,默认25(注意:属性名用_port,避免与getter/setter重名)
  ______
  ______: ______ = ______;

  constructor(
    id: number,
    address: string,
    server: string
  ) {
    this.id = id;
    this.address = address;
    this.smtpServer = server;
  }

  // 补全:port的getter(注意:getter名叫port,属性名叫_port)
  get ______(): ______ {
    return this._port;
  }

  // 补全:port的setter,验证范围1-65535
  set ______(value: ______) {
    if (value < 1 || value > 65535) {
      console.log("端口范围错误");
      return;
    }
    this._port = value;
  }
}
练习2答案
class EmailConfig {
  readonly id: number;
  address: string;
  private smtpServer: string;
  private _port: number = 25;

  constructor(
    id: number,
    address: string,
    server: string
  ) {
    this.id = id;
    this.address = address;
    this.smtpServer = server;
  }

  get port(): number {
    return this._port;
  }

  set port(value: number) {
    if (value < 1 || value > 65535) {
      console.log("端口范围错误");
      return;
    }
    this._port = value;
  }
}

练习3:编程题 - 实现一个计数器类

要求:

  1. 类名 Counter
  2. 静态属性 totalCounters 记录创建的实例总数
  3. 实例属性 id(readonly)、namecount
  4. 方法 increment() 增加count
  5. 方法 decrement() 减少count(不能小于0)
  6. 方法 reset() 重置count为0
  7. getter value 返回当前count
练习3答案
class Counter {
  static totalCounters: number = 0;
  readonly id: number;
  name: string;
  count: number = 0;

  constructor(id: number, name: string, count: number = 0) {
    Counter.totalCounters++;
    this.id = id;
    this.name = name;
    this.count = count;
  }

  increment(): void {
    this.count++;
  }

  decrement(): void {
    if (this.count > 0) {
      this.count--;
    }
  }

  reset(): void {
    this.count = 0;
  }

  get value(): number {
    return this.count;
  }
}

练习5:编程题 - 温度转换类

实现 Temperature 类:

  1. private属性 _celsius: number
  2. getter/setter celsius
  3. getter fahrenheit(只读,计算属性)
  4. getter kelvin(只读,计算属性)
  5. setter验证:celsius不能低于-273.15
练习5答案
class Temperature {
  private _celsius: number = 0;

  constructor(c: number) {
    if (c < -273.15) {
      console.log("温度不能低于绝对零度,初始化为0");
      this._celsius = 0;
      return;
    }
    this._celsius = c;
  }

  get celsius(): number {
    return this._celsius;
  }

  set celsius(c: number) {
    if (c < -273.15) {
      console.log("温度不能低于绝对零度");
      return;
    }
    this._celsius = c;
  }

  get fahrenheit(): number {
    return this._celsius * 9 / 5 + 32;
  }

  get kelvin(): number {
    return this._celsius + 273.15;
  }
}

练习6:编程题 - 单例模式(静态方法应用)

实现 ConfigManager 类:

  1. private static属性 _instance: ConfigManager | null = null
  2. public static方法 getInstance(): ConfigManager,返回唯一实例
  3. 实例属性 settings: Map<string, string>
  4. 实例方法 set(key: string, value: string): void
  5. 实例方法 get(key: string): string | undefined
练习6答案
class ConfigManager {
  private static _instance: ConfigManager | null = null;
  private settings: Map<string, string>;

  private constructor() {
    this.settings = new Map<string, string>();
  }

  static getInstance(): ConfigManager {
    if (ConfigManager._instance === null) {
      ConfigManager._instance = new ConfigManager();
    }
    return ConfigManager._instance;
  }

  set(key: string, value: string): void {
    this.settings.set(key, value);
  }

  get(key: string): string | undefined {
    return this.settings.get(key);
  }
}

练习7:编程题 - 带权限检查的设备类

实现 SecureDevice 类:

  1. readonly属性 deviceId: number
  2. private属性 _accessCode: string_isAuthorized: boolean = false
  3. 构造函数接收 deviceId 和 accessCode
  4. 方法 authorize(code: string): boolean,验证code是否正确
  5. 方法 operate(): string,只有授权后才能操作
  6. protected属性 _firmwareVersion: string = "1.0.0"
  7. protected方法 _updateFirmware(version: string): void
练习7答案
class SecureDevice {
  readonly deviceId: number;
  protected _firmwareVersion: string = "1.0.0";
  private _accessCode: string;
  private _isAuthorized: boolean = false;

  constructor(deviceId: number, accessCode: string) {
    this.deviceId = deviceId;
    this._accessCode = accessCode;
  }

  authorize(code: string): boolean {
    this._isAuthorized = (code === this._accessCode);
    return this._isAuthorized;
  }

  operate(): string {
    if (!this._isAuthorized) {
      return "鉴权失败,禁止操作";
    }
    return `设备 ${this.deviceId} 操作成功`;
  }

  protected _updateFirmware(version: string): void {
    if (version !== this._firmwareVersion) {
      this._firmwareVersion = version;
    }
  }
}

练习8:综合题 - 设备管理器

结合所学知识,实现一个简单的设备管理系统:

// 1. 定义设备类型枚举(字符串枚举)
enum DeviceType {
SENSOR = "sensor",
ACTUATOR = "actuator",
CONTROLLER = "controller"
}

// 2. 实现 Device 基类
//    - readonly id, name
//    - protected type: DeviceType
//    - private _isActive: boolean = false
//    - getter/setter active(setter中记录日志)
//    - 抽象方法 activate()/deactivate()(用空方法模拟)

// 3. 实现 DeviceManager 类
//    - static属性 devices: Device[] = []
//    - static方法 register(device: Device): void
//    - static方法 findById(id: number): Device | undefined
//    - static方法 getActiveCount(): number
//    - static方法 listAll(): string[]
练习8答案
enum DeviceType {
SENSOR = "sensor",
ACTUATOR = "actuator",
CONTROLLER = "controller"
}

class Device {
  readonly id: number;
  readonly name: string;
  protected type: DeviceType;
  private _isActive: boolean = false;

  constructor(id: number, name: string, type: DeviceType) {
    this.id = id;
    this.name = name;
    this.type = type;
  }

  get active(): boolean {
    return this._isActive;
  }

  set active(value: boolean) {
    console.log(`设备 ${this.id} 状态变为: ${value ? "激活" : "未激活"}`);
    this._isActive = value;
  }

  activate(): void {
    console.log(`设备 ${this.id} 正在激活...`);
    this.active = true;
  }

  deactivate(): void {
    console.log(`设备 ${this.id} 正在关闭...`);
    this.active = false;
  }
}

class DeviceManager {
  static devices: Device[] = [];

  static register(device: Device): void {
    if (DeviceManager.findById(device.id) === undefined) {
      DeviceManager.devices.push(device);
    }
  }

  static findById(id: number): Device | undefined {
    for (let i = 0; i < DeviceManager.devices.length; i++) {
      if (DeviceManager.devices[i].id === id) {
        return DeviceManager.devices[i];
      }
    }
    return undefined;
  }

  static getActiveCount(): number {
    let count = 0;
    for (let i = 0; i < DeviceManager.devices.length; i++) {
      if (DeviceManager.devices[i].active) {
        count++;
      }
    }
    return count;
  }

  static listAll(): string[] {
    let list: string[] = [];
    for (let i = 0; i < DeviceManager.devices.length; i++) {
      let d = DeviceManager.devices[i];
      list.push(`${d.id}: ${d.name} - ${d.active ? "激活" : "未激活"}`);
    }
    return list;
  }
}

附录:ArkTS Class 速查表

// 基础类定义
class MyClass {
  // 属性必须有初始值或在构造函数中赋值
  publicProp: number = 0;
  private _privateProp: string = "";
  protected protectedProp: boolean = false;
  readonly readOnlyProp: number;

  constructor(initialValue: number) {
    this.readOnlyProp = initialValue;
  }

  // 方法
  publicMethod(): void {
  }

  private _privateMethod(): void {
  }

  protected protectedMethod(): void {
  }
}

// 注意:ArkTS 不支持参数属性简写!
// 以下写法在 TypeScript 中合法,但在 ArkTS 中会报错:
// class ShortClass {
//     constructor(
//         public id: number,      // ❌ ArkTS 错误:arkts-no-ctor-prop-decls
//         private _secret: string,
//         readonly createdAt: number
//     ) {}
// }

// ✅ ArkTS 正确写法:先声明属性,再赋值
class ShortClass {
  id: number;
  private _secret: string;
  readonly createdAt: number;

  constructor(id: number, secret: string, createdAt: number) {
    this.id = id;
    this._secret = secret;
    this.createdAt = createdAt;
  }
}

// Getter / Setter
// 注意:ArkTS 中属性与 getter/setter 不能同名!
// 属性用 _前缀,getter/setter 用无前缀名
class WithAccessor {
  private _value: number = 0; // 属性名:_value

  get value(): number { // getter 名:value(不能叫 _value)
    return this._value;
  }

  set value(v: number) { // setter 名:value(不能叫 _value)
    this._value = v;
  }
}

// 静态成员
class StaticDemo {
  static count: number = 0;
  static readonly MAX: number = 100;

  static getCount(): number {
    return StaticDemo.count;
  }
}
posted @ 2026-04-14 14:22  thammer  阅读(7)  评论(0)    收藏  举报