4. ArkTS语言基础(下):面向对象编程、接口、泛型、模块化

好,咱们接着聊。上一节我们把ArkTS的基础语法过了一遍,这一节要上点强度了——面向对象、接口、泛型、模块化。这四个东西,说白了就是让你写的代码更“聪明”、更“好管”。

我刚开始学鸿蒙开发的时候,说实话,觉得面向对象有点绕。但后来在项目里写了一个上千行的页面,改一个需求要翻半天代码……嗯,从那以后,我再也不敢不用面向对象了。

核心一句话:面向对象帮你把代码“分类打包”,接口和泛型帮你“定规矩”,模块化帮你“分文件”。合在一起,你的代码就能像搭积木一样清晰。

4.1 面向对象编程:把现实世界搬进代码

面向对象编程(OOP)不是什么玄学。你想想看,现实世界里到处都是“对象”:你手里这部手机是一个对象,它有品牌、颜色、价格(属性),也能打电话、玩游戏(方法)。

在ArkTS里,我们用class来定义“类”,用new来创建“对象”。

// 定义一个“手机”类
class Phone {
  // 属性
  brand: string = '';
  price: number = 0;

  // 构造函数:创建对象时自动调用
  constructor(brand: string, price: number) {
    this.brand = brand;
    this.price = price;
  }

  // 方法
  call(name: string): void {
    console.log(`正在给${name}打电话...`);
  }
}

// 创建对象
let myPhone: Phone = new Phone('华为', 5999);
myPhone.call('小明'); // 输出:正在给小明打电话...

我个人习惯,写类的时候一定会把属性先声明好,哪怕给个默认值。不然ArkScript编译器会报错,别问我怎么知道的……

4.1.1 继承:少写重复代码

继承是OOP里最实用的特性。你有一个“手机”类,现在想做一个“智能手机”类,它除了能打电话,还能上网。难道要把Phone的代码再抄一遍?不用,用extends继承就行。

class SmartPhone extends Phone {
  // 新增属性
  os: string = 'HarmonyOS';

  constructor(brand: string, price: number, os: string) {
    super(brand, price); // 调用父类的构造函数
    this.os = os;
  }

  // 新增方法
  browseInternet(): void {
    console.log('正在浏览网页...');
  }

  // 重写父类方法
  call(name: string): void {
    super.call(name); // 调用父类方法
    console.log('使用VoLTE高清通话');
  }
}

let mySmartPhone: SmartPhone = new SmartPhone('华为', 6999, 'HarmonyOS 4.0');
mySmartPhone.call('小红');
// 输出:
// 正在给小红打电话...
// 使用VoLTE高清通话

避坑指南:我曾经在子类构造函数里忘了写super(),结果编译直接报错。记住:子类构造函数里,super()必须写在第一行,这是规矩。

4.1.2 访问修饰符:管好你的“隐私”

ArkTS提供了三个修饰符:public(公开)、private(私有)、protected(受保护)。

修饰符 本类内 子类内 外部
public
private
protected
class Account {
  private balance: number = 0; // 余额,只有自己能改

  public deposit(amount: number): void {
    if (amount > 0) {
      this.balance += amount;
    }
  }

  public getBalance(): number {
    return this.balance;
  }
}

let myAccount: Account = new Account();
myAccount.deposit(1000);
// myAccount.balance = 9999; // ❌ 编译错误,private属性外部不能访问
console.log(myAccount.getBalance()); // ✅ 输出:1000

为什么要有private?我在项目里见过有人直接改对象的内部数据,结果数据乱套了。用private把关键属性藏起来,只暴露安全的方法,这就是“封装”的精髓。

4.2 接口:定规矩,不写死

接口(Interface)是什么?说白了就是一份“合同”。它只规定你“必须有什么”,但不规定你“怎么实现”。

// 定义一个接口
interface Flyable {
  fly(): void;
  land(): void;
}

// 实现接口
class Bird implements Flyable {
  fly(): void {
    console.log('鸟儿扇动翅膀飞行');
  }

  land(): void {
    console.log('鸟儿落在树枝上');
  }
}

class Airplane implements Flyable {
  fly(): void {
    console.log('飞机喷气引擎飞行');
  }

  land(): void {
    console.log('飞机放下起落架降落');
  }
}

// 多态:同一个接口,不同实现
let flyingThings: Flyable[] = [new Bird(), new Airplane()];
flyingThings.forEach(item => item.fly());
// 输出:
// 鸟儿扇动翅膀飞行
// 飞机喷气引擎飞行

我的经验:接口在团队协作里特别好用。你定好接口,前端和后端各干各的,只要接口不变,谁改代码都不影响对方。我在做鸿蒙应用时,经常用接口来定义“数据模型”,比如用户信息、商品列表,这样整个项目的数据结构就统一了。

4.3 泛型:写一次,用多种类型

泛型(Generics)是ArkTS里一个很强大的工具。你想想看,有时候你写一个函数,它处理数字也行,处理字符串也行,难道要写两个函数?

不用,用泛型。

// 不用泛型:写死了类型
function getFirstElement(arr: number[]): number {
  return arr[0];
}

// 用泛型:什么类型都行
function getFirstElementGeneric<T>(arr: T[]): T {
  return arr[0];
}

let num = getFirstElementGeneric<number>([1, 2, 3]); // 类型是number
let str = getFirstElementGeneric<string>(['a', 'b', 'c']); // 类型是string

泛型还可以用在类和接口里:

// 泛型类
class Box<T> {
  private content: T;

  constructor(content: T) {
    this.content = content;
  }

  getContent(): T {
    return this.content;
  }
}

let numberBox = new Box<number>(42);
let stringBox = new Box<string>('Hello');

console.log(numberBox.getContent()); // 42
console.log(stringBox.getContent()); // Hello

避坑指南:我曾经在泛型里忘了写类型参数,结果TypeScript推断成了any,失去了类型检查。记住:泛型一定要显式或隐式地指定类型,不然就白写了。

4.4 模块化:把代码分到不同文件

模块化,说白了就是“分文件”。一个项目几百行代码全写在一个文件里,找起来累死人。ArkTS用exportimport来实现模块化。

4.4.1 导出和导入

// mathUtils.ets
export function add(a: number, b: number): number {
  return a + b;
}

export const PI: number = 3.14159;

export class Calculator {
  multiply(a: number, b: number): number {
    return a * b;
  }
}
// main.ets
import { add, PI, Calculator } from './mathUtils';

console.log(add(2, 3)); // 5
console.log(PI); // 3.14159

let calc = new Calculator();
console.log(calc.multiply(4, 5)); // 20

4.4.2 默认导出

每个文件只能有一个默认导出:

// Logger.ets
export default class Logger {
  log(message: string): void {
    console.log(`[LOG]: ${message}`);
  }
}
// main.ets
import Logger from './Logger'; // 默认导出不需要花括号

let logger = new Logger();
logger.log('应用启动成功');

注意:模块路径要写对。我刚开始时经常写错路径,比如忘了写./,结果编译报“找不到模块”。记住:相对路径一定要以./../开头。

4.5 本章知识体系

下面这张图,帮你把这一章的知识点串起来:

ArkTS语言基础(下)知识体系 面向对象编程 接口 泛型 模块化 类、继承、封装 访问修饰符 构造函数、方法 定义接口 实现接口 多态 泛型函数 泛型类 泛型接口 export导出 import导入 默认导出 核心目标:代码复用 + 类型安全 + 结构清晰 面向对象 → 接口 → 泛型 → 模块化,层层递进

这一节的内容确实有点多,但都是硬核干货。面向对象让你学会“分类”,接口让你学会“定规矩”,泛型让你学会“灵活”,模块化让你学会“分文件”。这四个东西合在一起,你的ArkTS代码就能从“能跑”变成“好维护”。

我个人建议,学完这一节后,找个简单的项目练练手。比如写一个“待办事项”应用,用类来表示任务,用接口来定义数据格式,用泛型来处理不同类型的列表,最后用模块化把代码分到不同文件。实践出真知,光看是学不会的。


专注资料整理