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用export和import来实现模块化。
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代码就能从“能跑”变成“好维护”。
我个人建议,学完这一节后,找个简单的项目练练手。比如写一个“待办事项”应用,用类来表示任务,用接口来定义数据格式,用泛型来处理不同类型的列表,最后用模块化把代码分到不同文件。实践出真知,光看是学不会的。