鸿蒙 ArkTS 类继承与多态实战:从语法到员工工资计算全指南

在鸿蒙应用开发中,ArkTS 作为核心开发语言,其面向对象特性是搭建可维护、可扩展项目的基础。而 "继承" 作为面向对象的核心能力之一,不仅能大幅减少重复代码,还能配合多态实现灵活的功能扩展。今天我们就从基础概念入手,结合真实的员工工资计算场景,拆解 ArkTS 继承的语法规则、实战技巧与避坑要点,帮你彻底掌握这一关键知识点。

一、搞懂继承:"子承父业" 的编程逻辑

提到继承,我们可以用 "子承父业" 来理解 ------ 就像子女能继承父母的部分技能与资源,在 ArkTS 中,子类(子对象)也能自动获取父类(父对象)的非私有属性和方法,无需重复编写相同逻辑。

比如开发企业管理系统时,"员工" 是所有岗位的共同身份:程序员、项目经理、前台都有 "姓名、性别、工资、奖金" 这些共性属性,也都需要 "展示个人信息""计算月收入" 这些共性行为。如果为每个岗位单独写一套代码,后续要新增 "工龄" 属性,就得修改所有岗位的类 ------ 而继承能解决这个问题:把共性内容抽成父类,子类只需关注自身的差异化逻辑。

继承的核心价值主要有三点,也是实际开发中架构设计的常用思路:

  • 代码重用:子类直接复用父类的非私有逻辑,减少重复代码量;
  • 多态灵活:同一方法在不同子类有不同实现,调用时自动匹配实际对象;
  • 模块解耦:结合接口编程降低模块依赖,后续扩展新功能更轻松。

二、ArkTS 继承基础:语法与核心规则

ArkTS 的继承语法与 Java、TypeScript 等主流语言相似,核心是 extends 关键字,但有几个关键规则需要注意,尤其是 "继承范围" 和 "方法重写",这是新手最易踩坑的地方。

1. 基本语法:子类如何继承父类?

我们先定义一个 "员工父类",再让 "程序员子类" 继承它,直观感受语法结构:

typescript 复制代码
// 父类:员工(Employee),封装所有岗位的共性
export class Employee {
  // 成员属性:默认public(公开,子类可继承)
  name: string; // 姓名
  sex: string; // 性别
  sal: number; // 基本工资
  comm: number; // 奖金

  // 构造方法:初始化属性(ArkTS要求所有字段必须显式初始化)
  constructor(name: string, sex: string, sal: number, comm: number) {
    this.name = name;
    this.sex = sex;
    this.sal = sal;
    this.comm = comm;
  }

  // 父类方法:展示员工基本信息
  show(): void {
    console.log(`姓名:${this.name}, 性别:${this.sex}, 基本工资:${this.sal}, 奖金:${this.comm}`);
  }

  // 父类方法:计算月收入(默认逻辑:基本工资+奖金)
  getIncome(): number {
    return this.sal + this.comm;
  }
}

// 子类:程序员(Programmer),继承自Employee
export class Programmer extends Employee {
  // 子类自动拥有父类的非私有属性(name/sex/sal/comm)和方法(show()/getIncome())
}

2. 关键限制:私有成员 "不可继承"

需要特别注意:父类中用 private(私有)修饰的成员,子类无法继承和访问。比如曾尝试将 "基本工资(sal)" 设为 private,结果子类调用该属性时均报错 ------ 最终只能将其改为默认的 public,才能确保子类正常使用这些属性(原文此处配有报错截图)。

3. 核心能力:子类可 "重写" 父类方法

继承的一大优势是 "子类能改进父类方法",也就是 "方法重写"。比如父类默认的收入计算是 "工资 + 奖金",但程序员有额外的技术补贴,这时就可以在子类中重写 getIncome() 方法,扩展计算逻辑。

就像生活中 "父亲用传统方式卖油饼,子女用自动化设备卖油饼",核心行为一致,但实现方式更优。以程序员为例,重写代码如下:

typescript 复制代码
export class Programmer extends Employee {
  // 重写父类的getIncome(),添加技术补贴
  override getIncome(): number {
    return this.sal + this.comm + 1500; // 程序员额外加1500元技术补贴
  }
}

这里的 override 关键字可选,但显式添加能让代码更规范,明确表示 "该方法是重写父类的实现"。

三、实战案例:用员工工资计算吃透继承与重写

最能体现继承价值的场景,就是 "同一类型下的差异化功能"------ 比如不同岗位的员工,工资计算规则不同。我们以 "4 类员工的收入计算" 为例,完整演示继承的实战用法,所有逻辑和数据均来自实际开发场景的典型设计。

1. 需求背景

公司有 4 类员工,工资计算规则存在差异:

  • 程序员:基本工资 + 奖金 + 1500 元(技术补贴);
  • 项目经理:基本工资 + 奖金 + 5000 元(管理奖);
  • 测试人员:基本工资 + 奖金 + 800 元(测试补贴);
  • 前台:基本工资 + 奖金(无额外补贴,沿用父类逻辑)。

2. 子类实现:只关注 "差异化" 逻辑

因为父类已经封装了共性(属性、show() 方法、默认 getIncome()),子类只需针对 "收入计算" 这一差异化点,重写 getIncome() 即可,代码极简:

typescript 复制代码
// 1. 项目经理类(Manager)
export class Manager extends Employee {
  override getIncome(): number {
    return this.sal + this.comm + 5000; // 加5000元管理奖
  }
}

// 2. 测试人员类(Tester)
export class Tester extends Employee {
  override getIncome(): number {
    return this.sal + this.comm + 800; // 加800元测试补贴
  }
}

// 3. 前台类(Fronter)
export class Fronter extends Employee {
  // 无额外补贴,直接复用父类逻辑(也可显式重写,保持代码统一)
  override getIncome(): number {
    return super.getIncome(); // super关键字:调用父类的方法
  }
}

这里的 super 关键字很实用:当子类想复用父类方法,再补充自己的逻辑时,用 super.方法名() 就能直接调用父类实现,避免重复代码。比如后续前台要加 "全勤奖",只需改成 return super.getIncome() + 300,灵活又高效。

3. 效果验证:创建对象并计算收入

定义好类后,我们创建不同岗位的员工对象,调用 getIncome() 计算收入,看看继承和重写是否生效,运行后会看到弹窗结果完全符合预期:

这就是继承的优势:父类定好 "框架",子类填充 "差异",代码简洁且易维护。

四、多态:继承的 "进阶技能",让代码更灵活

继承的核心价值之一是实现 "多态"------ 简单说就是 "父类引用指向子类对象,调用方法时取决于实际对象类型"。这一特性能让代码更灵活,尤其适合 "批量处理同一类型下的不同对象"。

1. 多态的具体体现

我们定义一个 Employee 类型的数组,里面存放不同子类的对象,然后遍历数组统一调用 getIncome()

typescript 复制代码
// 父类引用数组,存放不同子类对象
const employees: Employee[] = [
  new Programmer('阿朱', '女', 9000, 100),
  new Manager('Ken', '男', 40000, 1000),
  new Tester('阿紫', '女', 11000, 200),
  new Fronter('小丽', '女', 6000, 1000)
];

// 遍历数组,统一计算所有员工总收入
let totalIncome = 0;
employees.forEach(emp => {
  totalIncome += emp.getIncome();
  console.log(`${emp.name} 的月收入:${emp.getIncome()}元`);
});
console.log(`所有员工月总收入:${totalIncome}元`);

这里的关键是:虽然 emp 的类型是 Employee,但调用 emp.getIncome() 时,会自动匹配 emp 实际指向的子类(比如 ProgrammerManager),执行对应的重写方法 ------ 这就是多态。

2. 多态的优势:易扩展,符合 "开闭原则"

如果后续公司新增 "钟点工" 岗位,只需新增 PartTimer 子类并重写 getIncome(),无需修改遍历数组的逻辑:

typescript 复制代码
// 新增:钟点工类
export class PartTimer extends Employee {
  override getIncome(): number {
    return this.sal * 22; // 假设钟点工按"日薪×22天"计算
  }
}

// 直接添加到数组,无需修改遍历逻辑
employees.push(new PartTimer('老王', '男', 300, 0));

这种 "对扩展开放,对修改关闭" 的特性,是多态的核心价值,也是大型项目能持续迭代的关键。

五、避坑指南:构造方法的 "隐形规则"

在 ArkTS 继承中,构造方法是最容易出错的地方,有两个核心规则必须牢记,否则会频繁报编译错误。

规则 1:子类构造必须先调用父类构造(super)

ArkTS 有个 "隐形要求":子类的构造方法中,必须先调用父类的构造方法(用 super()),否则会报错。这就像 "孩子出生前,父母必须先存在" 的逻辑。

如果父类有无参构造 (默认或显式定义),子类构造会自动调用 super(),无需手动写:

typescript 复制代码
// 父类:显式定义无参构造
export class Employee {
  name: string = '';
  sex: string = '';
  sal: number = 0;
  comm: number = 0;
  constructor() {} // 无参构造
}

// 子类:构造自动调用super()
export class Programmer extends Employee {
  constructor(name: string) {
    // 无需手动写super(),ArkTS自动调用父类无参构造
    this.name = name; // 初始化父类继承的name属性
  }
}

规则 2:父类有有参构造时,子类必须显式传参

如果父类只有有参构造 (没有无参构造),子类必须在构造中显式调用 super(参数),传递父类需要的参数,否则会报错:

typescript 复制代码
// 父类:只有有参构造(无无参构造)
export class Employee {
  name: string;
  sex: string;
  sal: number;
  comm: number;

  // 有参构造,无无参构造
  constructor(name: string, sex: string, sal: number, comm: number) {
    this.name = name;
    this.sex = sex;
    this.sal = sal;
    this.comm = comm;
  }
}

// 子类:必须显式调用super(参数)
export class Programmer extends Employee {
  constructor(name: string, sex: string, sal: number, comm: number) {
    super(name, sex, sal, comm); // 必须调用,否则报错
  }
}

如果子类想自定义构造参数(比如只传姓名和性别,工资奖金默认 0),也可以在 super 中填默认值:

typescript 复制代码
export class Programmer extends Employee {
  // 子类构造只接收2个参数
  constructor(name: string, sex: string) {
    // 调用父类有参构造,工资奖金默认0
    super(name, sex, 0, 0);
  }
}

额外提醒:ArkTS 字段必须初始化

ArkTS 有 "空安全" 特性,要求所有字段(父类和子类)必须在声明时或构造中显式初始化(比如 name: string = ''constructor 中赋值),否则会报错。这是为了避免后续调用时出现空指针问题,和 TypeScript 的严格模式逻辑一致。

六、总结

今天我们从 "概念→语法→实战→多态→避坑",完整梳理了 ArkTS 类继承的核心知识点:

  1. 继承是 "子承父业",核心价值是代码重用、多态和解耦;
  2. extends 实现继承,private 成员不可继承,override 可重写父类方法;
  3. 多态通过 "父类引用指向子类对象" 实现,灵活支持功能扩展;
  4. 子类构造必须调用父类构造(super),父类有参构造时需显式传参。

继承是 ArkTS 面向对象的基础,也是后续学习自定义组件、状态管理的关键。掌握这些知识点,能让你在鸿蒙开发中写出更简洁、更易维护的代码。如果觉得有用,欢迎分享给身边一起学习鸿蒙的朋友~

当然也欢迎大家一起系统地学习鸿蒙开发,深入掌握类和对象、继承多态等 ArkTS 面向对象核心技术,拿下鸿蒙基础、高级开发者证书,欢迎加入我的鸿蒙班,一起从入门到精通!班级链接:点击免费进入

相关推荐
shenshizhong2 小时前
看懂鸿蒙系统源码 比较重要的知识点
android·harmonyos
特立独行的猫a5 小时前
强大的鸿蒙HarmonyOS网络调试工具PageSpy 介绍及使用
网络·华为·harmonyos
ChinaDragon5 小时前
HarmonyOS:在NDK工程中使用预构建库
harmonyos
程序员潘Sir8 小时前
鸿蒙应用开发从入门到实战(三):第一个鸿蒙应用
harmonyos·鸿蒙
安卓开发者9 小时前
鸿蒙NEXT中SQLite数据库全面实战指南
数据库·sqlite·harmonyos
HarderCoder10 小时前
仓颉语言Option 的“问号”全景图—— 一个 `?` 与 `.` `()` `[]` `{}` 的组合写法
harmonyos
威哥爱编程21 小时前
HarmonyOS 5.1手势事件详解
harmonyos
HarderCoder1 天前
使用仓颉语言理解 SOLID 原则:概念、实战与踩坑总结
harmonyos
爱笑的眼睛111 天前
HarmonyOS 应用开发深度解析:ArkTS 声明式 UI 与状态管理最佳实践
华为·harmonyos