散修带你入门鸿蒙应用开发基础第十四节:抽象与面向接口编程
第十四节:抽象与面向接口编程
炼气十四重天
【学习目标】
- 理解接口(
interface)的核心价值(能力规范+多维度扩展),掌握ArkTS中接口的定义与实现语法; - 掌握“多接口实现”的标准写法,解决ArkTS“单继承”的核心限制;
- 吃透接口的进阶特性(可选属性、接口继承),适配鸿蒙复杂业务场景;
- 掌握“面向接口编程”的核心思想,实现“依赖抽象而非具体实现”的工程化设计;
- 明确抽象类与接口的选型原则,结合鸿蒙电商场景落地“抽象类定基类、接口定能力”的架构。
【学习重点】
- 接口核心语法:
interface定义规范、implements实现接口、多接口实现的语法规则; - 接口进阶特性:可选属性(
?)、接口继承(extends); - 面向接口编程:接口作为参数/返回值类型,统一调用逻辑,降低代码耦合;
- 抽象类 vs 接口:“是不是”用抽象类(继承),“有没有”用接口(实现);
- ArkTS语法约束:
instanceof/typeof支持、is/in/&禁用、类型别名(type)的使用限制; - 鸿蒙实战:为商品扩展“可秒杀”“可退换”能力,复用抽象类基础,实现抽象类+多接口的灵活架构。
一、工程结构
复制第十三节的ClassObjectDemo_2工程,重命名为ClassObjectDemo_3,新增接口(IReturnable、IPromotion、ISeckill),核心目录如下:
ClassObjectDemo_3/
├── entry/ # 应用主模块
│ ├── src/main/ets/
│ │ ├── pages/
│ │ │ └── Index.ets # 测试页面(接口+面向接口编程)
│ │ └── model/
│ │ ├── AbstractGoods.ets # 抽象父类:商品基类(定基类)
│ │ ├── capability/ # 鸿蒙规范:能力接口目录
│ │ │ ├── IReturnable.ets # 接口:可退换能力
│ │ │ ├── IPromotion.ets # 接口:基础促销能力
│ │ │ └── ISeckill.ets # 接口:可秒杀能力(继承IPromotion)
│ │ ├── PhysicalGoods.ets # 子类:实体商品(实现IReturnable)
│ │ ├── VirtualGoods.ets # 子类:虚拟商品(无额外能力)
│ │ └── SeckillGoods.ets # 子类:秒杀商品(实现ISeckill+IReturnable)
│ ├── resources/ # 资源目录
│ └── module.json5 # 模块配置(API12适配)
└── hvigorfile.ts # 构建脚本
二、为什么需要接口?突破单继承限制
2.1 单继承的核心痛点
ArkTS仅支持单继承,即一个类只能继承一个父类。在电商场景中,商品的能力维度是多样的:实体商品需要“可退换”能力,秒杀商品需要“可秒杀+可退换”能力,仅依赖继承无法实现这种多能力组合扩展。
2.2 接口的核心定位
接口的核心价值是定义能力规范,不关注类的具体类型,只关注类具备的能力。一个类可以同时实现多个接口,从而突破单继承的限制,灵活扩展多维度能力。
- 接口仅定义“能力的规范”(属性名/类型、方法名/参数/返回值),仅支持方法声明,无任何实现逻辑;
- 类通过
implements关键字实现接口,必须严格遵守规范,实现所有必选的属性和方法; - 类实现接口时,可同时复用父类(抽象类)的已有实现逻辑。
三、声明接口核心语法(interface)
3.1 接口定义语法
// ArkTS 接口名采用大驼峰命名,以I开头避免重名
export interface ICapabilityName {
// 必选属性:无初始值,仅约束类型
属性名称: 数据类型;
// 可选属性(?):非必须实现,适配灵活场景
属性名称?: 数据类型;
// 普通方法:仅定义规范(声明),无实现体
methodName(param: 数据类型): 返回值;
}
// 接口继承:整合多个接口规范,形成更完整的能力定义
export interface IChildCapability extends IParentCapability1, IParentCapability2 {
// 新增专属的属性/方法规范
}
3.2 类实现接口语法(implements)
// 实现单个接口(继承抽象基类+实现接口,复用父类已有逻辑)
export class ClassName extends AbstractBaseClass implements ICapability {
// 实现抽象类抽象方法
abstractMethod(): void {}
// 必须实现接口的所有必选属性
propertyName: 数据类型;
// 必须实现接口的所有方法(实现具体逻辑)
methodName(param: 数据类型): 返回值 {
}
}
// 实现多个接口(核心:突破单继承限制)
export class ClassName extends AbstractBaseClass implements ICapability1, ICapability2 {
// 实现抽象类的抽象方法
abstractMethod(): void {}
// 实现接口1的所有必选成员
// 实现接口2的所有必选成员
}
3.3 实战1:定义商品能力接口
新增:基础促销能力(IPromotion)
// model/capability/IPromotion.ets
/**
* 基础促销能力接口
* 定义:所有促销商品的通用规范
* 场景:秒杀、满减等促销商品的基础能力
*/
export interface IPromotion {
// 必选属性:促销折扣率(0~1)
discountRate: number;
// 必选属性:促销开始时间
promoStartTime: Date;
// 方法声明:校验促销时间是否有效
checkPromoTimeValid(): boolean;
}
新增:可退换能力(IReturnable)
// model/capability/IReturnable.ets
/**
* 可退换能力接口
* 定义:具备退换能力的商品需实现的规范
* 场景:实体商品/秒杀商品需实现此接口
*/
export interface IReturnable {
// 必选属性:退换期限(天)
returnDays: number;
// 可选属性:是否支持无理由退换
isSevenDaysReturn?: boolean;
// 方法声明:校验商品是否可退换
checkReturnable(): boolean;
// 方法声明:获取退换说明
getReturnDesc(): string;
}
新增:可秒杀能力(ISeckill,继承IPromotion)
// model/capability/ISeckill.ets
import { IPromotion } from "./IPromotion";
/**
* 可秒杀能力接口
* 定义:具备秒杀能力的商品需实现的规范
* 场景:秒杀商品专属能力(继承基础促销能力)
*/
export interface ISeckill extends IPromotion {
// 必选属性:秒杀库存
seckillStock: number;
// 方法声明:计算商品秒杀价
calculateSeckillPrice(): number;
}
四、实战2:类实现多接口
4.1 抽象商品基类(AbstractGoods.ets)
// model/AbstractGoods.ets
/**
* 商品抽象基类
* 核心:封装商品通用属性和方法,强制子类实现核心业务逻辑
*/
export abstract class AbstractGoods {
public name: string; // 商品名称
public category: string = "鸿蒙周边";
protected _basePrice: number; // 商品底价(protected仅子类可访问)
public profitRate: number; // 利润率
/**
* 构造函数
* @param name 商品名称
* @param basePrice 商品底价
* @param category 商品分类(可选)
* @param profitRate 利润率(默认10%)
*/
constructor(
name: string,
basePrice: number,
category?: string,
profitRate: number = 0.1
) {
this.name = name;
this._basePrice = basePrice > 0 ? basePrice : 0; // 底价非负校验
this.profitRate = profitRate > 0 ? profitRate : 0.1; // 利润率非负校验
this.category = category?.trim() || this.category;
}
// 底价读写器:控制底价的修改逻辑
get basePrice() {
return this._basePrice;
}
set basePrice(newPrice: number) {
if (newPrice < 0) {
console.log(`【${this.name}】商品底价不能为负,修改失败`);
return;
}
this._basePrice = newPrice;
}
// 基础信息打印方法
printBaseInfo(): void {
console.log(`
商品名称:${this.name}
商品分类:${this.category}
商品底价:${this._basePrice}元
利润利率:${this.profitRate * 100}%
`);
}
// 抽象方法:强制子类实现售价计算
abstract calculateSellingPrice(): number;
// 抽象方法:强制子类实现库存校验
abstract checkStock(amount: number): boolean;
}
4.2 实体商品(PhysicalGoods.ets)
// model/PhysicalGoods.ets
import { AbstractGoods } from './AbstractGoods';
import { IReturnable } from './capability/IReturnable';
/**
* 实体商品类
* 继承:AbstractGoods(商品基类)
* 实现:IReturnable(可退换能力)
*/
export class PhysicalGoods extends AbstractGoods implements IReturnable {
public stock: number; // 库存(实体商品专属属性)
public weight: number; // 重量(kg,实体商品专属属性)
// 实现IReturnable接口必选属性
returnDays: number = 7; // 默认7天退换
isSevenDaysReturn: boolean = true; // 默认支持无理由退换
/**
* 构造函数
* @param name 商品名称
* @param basePrice 商品底价
* @param stock 库存数量
* @param weight 商品重量
* @param category 商品分类(可选)
* @param profitRate 利润率(默认10%)
*/
constructor(
name: string,
basePrice: number,
stock: number,
weight: number,
category?: string,
profitRate: number = 0.1
) {
super(name, basePrice, category, profitRate);
this.stock = Math.max(stock, 0); // 库存非负处理
this.weight = Math.max(weight, 0); // 重量非负处理
}
// 实现抽象类方法:计算实体商品售价(底价+利润率)
override calculateSellingPrice(): number {
return parseFloat((this._basePrice * (1 + this.profitRate)).toFixed(2));
}
// 重写父类的printBaseInfo方法:整合基础信息+实体专属信息+退换信息
override printBaseInfo(): void {
super.printBaseInfo();
// 实体商品专属基础信息
console.log(`
库存数量:${this.stock}件
商品重量:${this.weight.toFixed(2)}kg
`);
// 实体商品退换能力信息
const returnDesc = this.getReturnDesc();
console.log(`
【实体商品退换信息】
退换规则:${returnDesc}
当前可退换:${this.checkReturnable() ? "✅ 是" : "❌ 否"}
`);
}
// 实现IReturnable接口方法:校验是否可退换(库存>0则可退换)
public checkReturnable(): boolean {
return this.stock > 0;
}
// 实现IReturnable接口方法:生成退换说明
public getReturnDesc(): string {
const reasonDesc = this.isSevenDaysReturn ? "支持无理由" : "不支持无理由";
return `${this.name}:${reasonDesc},退换期限${this.returnDays}天`;
}
/**
* 实现抽象类的checkStock方法:实体商品库存校验
* @param amount 购买数量
*/
override checkStock(amount: number): boolean {
if (amount <= 0) {
console.log(`【${this.name}】校验失败:购买数量必须大于0`);
return false;
}
if (amount > this.stock) {
console.log(`【${this.name}】校验失败:库存不足,当前库存${this.stock}件,请求${amount}件`);
return false;
}
console.log(`【${this.name}】库存校验通过,剩余库存${this.stock}件`);
return true;
}
/**
* 个性方法:基于库存校验的扣减逻辑
* @param amount 要扣减的库存数量
*/
reduceStock(amount: number): void {
if (this.checkStock(amount)) {
this.stock -= amount;
console.log(`【${this.name}】库存扣减成功,剩余库存:${this.stock}件`);
}
}
}
4.3 秒杀商品(SeckillGoods.ets)
// model/SeckillGoods.ets
import { AbstractGoods } from './AbstractGoods';
import { IReturnable } from './capability/IReturnable';
import { ISeckill } from './capability/ISeckill';
/**
* 秒杀商品类
* 继承:AbstractGoods(商品基类)
* 实现:ISeckill(可秒杀能力)+ IReturnable(可退换能力)
* 核心:突破单继承,实现双能力扩展
*/
export class SeckillGoods extends AbstractGoods implements ISeckill, IReturnable {
// 实现ISeckill接口属性(含继承自IPromotion的属性)
discountRate: number;
seckillStock: number;
promoStartTime: Date; // 秒杀开始时间(IPromotion接口属性)
seckillEndTime: Date; // 秒杀结束时间(配套属性,增强业务逻辑)
// 实现IReturnable接口属性(秒杀商品特殊规则)
returnDays: number = 3; // 仅3天退换期
isSevenDaysReturn: boolean = false; // 不支持无理由退换
/**
* 构造函数
* @param name 商品名称
* @param basePrice 商品底价
* @param seckillStock 秒杀库存
* @param discountRate 秒杀折扣率
* @param promoStartTime 秒杀开始时间
* @param seckillEndTime 秒杀结束时间
* @param category 商品分类(可选,默认“秒杀商品”)
* @param profitRate 利润率(默认10%)
*/
constructor(
name: string,
basePrice: number,
seckillStock: number,
discountRate: number,
promoStartTime: Date,
seckillEndTime: Date,
category?: string,
profitRate: number = 0.1
) {
super(name, basePrice, category || "秒杀商品", profitRate);
// 库存非负处理
this.seckillStock = Math.max(seckillStock, 0);
// 折扣率约束:0.1~1(最低1折,最高原价)
this.discountRate = Math.max(0.1, Math.min(discountRate, 1));
// 秒杀时间校验:开始时间不能早于当前时间,结束时间不能早于开始时间
const now = new Date();
this.promoStartTime = promoStartTime < now ? now : promoStartTime;
this.seckillEndTime = seckillEndTime < this.promoStartTime ? new Date(this.promoStartTime.getTime() + 24 * 3600 * 1000) : seckillEndTime;
}
// 实现抽象类方法:计算秒杀商品常规售价
override calculateSellingPrice(): number {
return parseFloat((this._basePrice * (1 + this.profitRate)).toFixed(2));
}
// 重写父类的printBaseInfo方法:整合基础信息+秒杀专属信息+能力信息
override printBaseInfo(): void {
super.printBaseInfo();
// 秒杀商品专属基础信息
console.log(`
秒杀库存:${this.seckillStock}件
秒杀折扣:${(this.discountRate * 10).toFixed(1)}折
秒杀时段:${this.promoStartTime.toLocaleString()} - ${this.seckillEndTime.toLocaleString()}
`);
// 秒杀商品能力+退换信息
const returnDesc = this.getReturnDesc();
const promoValid = this.checkPromoTimeValid();
const seckillPrice = this.calculateSeckillPrice();
console.log(`
【秒杀商品能力信息】
秒杀价:${seckillPrice.toFixed(2)}元(原价:${this.calculateSellingPrice().toFixed(2)}元)
秒杀时间有效:${promoValid ? "✅ 是" : "❌ 否"}
退换规则:${returnDesc}
当前可退换:${this.checkReturnable() ? "✅ 是" : "❌ 否"}
`);
}
// 实现IReturnable接口方法:秒杀商品仅非秒杀时段可退换
public checkReturnable(): boolean {
return !this.checkPromoTimeValid() && this.seckillStock > 0;
}
// 实现IReturnable接口方法:生成秒杀商品退换说明
public getReturnDesc(): string {
const reasonDesc = this.isSevenDaysReturn ? "支持无理由" : "不支持无理由";
return `${this.name}:${reasonDesc},退换期限${this.returnDays}天`;
}
// 实现ISeckill接口方法:计算秒杀价
public calculateSeckillPrice(): number {
const normalPrice = this.calculateSellingPrice();
return parseFloat((normalPrice * this.discountRate).toFixed(2));
}
// 实现IPromotion接口方法:校验促销(秒杀)时间是否有效
public checkPromoTimeValid(): boolean {
const now = new Date();
const timeValid = now >= this.promoStartTime && now <= this.seckillEndTime;
if (!timeValid) {
if (now < this.promoStartTime) {
console.log(`【${this.name}】秒杀尚未开始:距离开始还有${Math.ceil((this.promoStartTime.getTime() - now.getTime()) / (60 * 1000))}分钟`);
} else {
console.log(`【${this.name}】秒杀已结束:已超过结束时间${Math.ceil((now.getTime() - this.seckillEndTime.getTime()) / (60 * 1000))}分钟`);
}
}
return timeValid;
}
/**
* 实现抽象类的checkStock方法:秒杀商品库存校验
* @param amount 购买数量
*/
override checkStock(amount: number): boolean {
if (amount <= 0) {
console.log(`【${this.name}】秒杀库存校验失败:购买数量必须大于0`);
return false;
}
// 先校验秒杀时间是否有效
if (!this.checkPromoTimeValid()) {
console.log(`【${this.name}】秒杀库存校验失败:当前非秒杀时段`);
return false;
}
if (amount > this.seckillStock) {
console.log(`【${this.name}】秒杀库存校验失败:库存不足,当前秒杀库存${this.seckillStock}件,请求${amount}件`);
return false;
}
console.log(`【${this.name}】秒杀库存校验通过,剩余秒杀库存${this.seckillStock}件`);
return true;
}
/**
* 秒杀商品专属方法:扣减秒杀库存
* @param amount 扣减数量
*/
public reduceSeckillStock(amount: number): void {
if (this.checkStock(amount)) {
this.seckillStock -= amount;
console.log(`【${this.name}】秒杀库存扣减成功:剩余${this.seckillStock}件`);
} else {
console.log(`【${this.name}】秒杀库存扣减失败`);
}
}
}
4.4 虚拟商品(VirtualGoods.ets)
// model/VirtualGoods.ets
import { AbstractGoods } from './AbstractGoods';
/**
* 虚拟商品类
* 继承:AbstractGoods(商品基类)
* 特点:无额外能力接口实现
*/
export class VirtualGoods extends AbstractGoods {
public validDays: number; // 有效期(天)
public isAutoRenew: boolean; // 是否自动续费
/**
* 构造函数
* @param name 商品名称
* @param basePrice 商品底价
* @param validDays 有效期
* @param isAutoRenew 是否自动续费
* @param category 商品分类(可选)
* @param profitRate 利润率(默认15%)
*/
constructor(
name: string,
basePrice: number,
validDays: number,
isAutoRenew: boolean,
category?: string,
profitRate: number = 0.15
) {
super(name, basePrice, category, profitRate);
this.validDays = Math.max(validDays, 1); // 有效期至少1天
this.isAutoRenew = isAutoRenew;
}
// 实现抽象类方法:计算虚拟商品售价(底价+利润率)
override calculateSellingPrice(): number {
return parseFloat((this._basePrice * (1 + this.profitRate)).toFixed(2));
}
// 重写父类的printBaseInfo方法:整合基础信息+虚拟专属信息+提示信息
override printBaseInfo(): void {
super.printBaseInfo();
// 虚拟商品专属基础信息
console.log(`
有效期:${this.validDays}天
自动续费:${this.isAutoRenew ? "✅ 开启" : "❌ 关闭"}
`);
// 虚拟商品提示信息
console.log(`
【虚拟商品提示】
温馨提示:虚拟商品不支持退换,购买前请确认
`);
}
/**
* 实现抽象类的checkStock方法:虚拟商品无库存限制
* @param amount 购买数量
*/
override checkStock(amount: number): boolean {
if (amount <= 0) {
console.log(`【${this.name}】校验失败:购买数量必须大于0`);
return false;
}
console.log(`【${this.name}】虚拟商品无库存限制,校验通过`);
return true;
}
}
五、面向接口编程:依赖抽象而非具体实现
5.1 核心思想
“面向接口编程”是将接口作为代码交互的“契约”,让业务逻辑依赖接口而非具体的类,核心优势:
- 降低耦合:修改类内部实现无需改动调用方;
- 易扩展:新增类实现接口即可兼容现有逻辑;
- 易测试:可通过模拟类快速完成单元测试;
- 鸿蒙工程化价值:适配大型项目的模块化、可插拔设计。
5.2 实战:接口作为参数/返回值(鸿蒙电商场景)
以下是完整的UI测试页面代码,适配API12语法规范:
// pages/Index.ets
import { PhysicalGoods } from '../model/PhysicalGoods';
import { VirtualGoods } from '../model/VirtualGoods';
import { SeckillGoods } from '../model/SeckillGoods';
import { AbstractGoods } from '../model/AbstractGoods';
import { LengthMetrics } from '@kit.ArkUI';
/**
* 商品管理页面(接口+面向接口编程)
*/
@Entry
@Component
struct Index {
@State goodsList: AbstractGoods[] = [
new PhysicalGoods("鸿蒙Mate70手机", 6999, 200, 0.2, "智能手机"),
new VirtualGoods("鸿蒙VIP会员", 99, 365, true),
new SeckillGoods("鸿蒙智能手表GT5(秒杀款)", 1299, 50, 0.7, new Date(), new Date(Date.now() + 3600 * 1000 * 2))];
/**
* 统一执行商品核心逻辑
*/
private callSubclassMethods(): void {
console.log("===== 鸿蒙电商商品核心逻辑执行 =====");
this.goodsList.forEach((goods, index) => {
console.log(`\n【商品${index + 1}:${goods.name}】`);
goods.printBaseInfo();
console.log(`最终售价:${goods.calculateSellingPrice()}元`);
// 库存校验
const checkResult = goods.checkStock(2);
console.log(`购买2件库存校验结果:${checkResult ? "✅ 通过" : "❌ 失败"}`);
// 调用子类专属方法(instanceof 判断)
if (goods instanceof PhysicalGoods) {
goods.reduceStock(2);
} else if (goods instanceof SeckillGoods) {
goods.reduceSeckillStock(2);
}
});
// 触发UI刷新
this.goodsList = [...this.goodsList];
}
/**
* 扣减实体商品库存
*/
private handleReduceStock(goods: AbstractGoods, amount: number = 1): void {
if (goods instanceof PhysicalGoods) {
console.log(`\n===== 执行${goods.name}:扣减库存${amount}件 =====`);
goods.reduceStock(amount);
this.goodsList = [...this.goodsList];
} else {
console.log(`\n❌ ${goods.name}不是实体商品,无法扣减库存`);
}
}
/**
* 扣减秒杀商品库存
*/
private handleReduceSeckillStock(goods: AbstractGoods, amount: number = 1): void {
if (goods instanceof SeckillGoods) {
console.log(`\n===== 执行${goods.name}:扣减秒杀库存${amount}件 =====`);
goods.reduceSeckillStock(amount);
this.goodsList = [...this.goodsList];
} else {
console.log(`\n❌ ${goods.name}不是秒杀商品,无法扣减秒杀库存`);
}
}
/**
* 购买虚拟商品
*/
private handleBuyVirtualGoods(goods: AbstractGoods): void {
if (goods instanceof VirtualGoods) {
console.log(`\n===== 执行${goods.name}:购买操作 =====`);
console.log(`${goods.name}购买成功!有效期:${goods.validDays}天 | 自动续费:${goods.isAutoRenew ? '开启' : '关闭'}`);
} else {
console.log(`\n❌ ${goods.name}不是虚拟商品,无法执行购买操作`);
}
}
/**
* 格式化日期为友好的字符串
*/
private formatDate(date: Date): string {
return date.toLocaleString('zh-CN', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit'
});
}
build() {
Column() {
// 页面标题
Text("抽象类+接口 商品管理实战")
.fontSize(22)
.fontWeight(FontWeight.Bold)
.margin(20);
// 全局操作按钮
Button("执行所有商品核心操作")
.width('90%')
.height(40)
.backgroundColor(Color.Blue)
.fontColor(Color.White)
.borderRadius(8)
.margin({ bottom: 10 })
.onClick(() => this.callSubclassMethods());
Scroll() {
Column() {
// 统一渲染所有商品(多态渲染)
ForEach(this.goodsList, (goods: AbstractGoods) => {
Column() {
// 商品基础信息
Text(goods.name)
.fontSize(18)
.fontWeight(FontWeight.Medium);
Text(`分类:${goods.category}`)
.fontSize(14)
.margin(2)
.fontColor('#666666');
Text(`底价:¥${goods.basePrice} | 利润率:${(goods.profitRate * 100).toFixed(1)}%`)
.fontSize(14)
.margin(2)
.fontColor('#666666');
Text(`售价:¥${goods.calculateSellingPrice().toFixed(2)}`)
.fontSize(16)
.fontColor(Color.Red)
.margin(5);
// 渲染子类专属信息
if (goods instanceof PhysicalGoods) {
Text(`库存:${goods.stock}件 | 重量:${goods.weight.toFixed(2)}kg`)
.fontSize(12)
.margin(2)
.fontColor('#999999');
} else if (goods instanceof VirtualGoods) {
Text(`有效期:${goods.validDays}天 | 自动续费:${goods.isAutoRenew ? '开启' : '关闭'}`)
.fontSize(12)
.margin(2)
.fontColor('#999999');
} else if (goods instanceof SeckillGoods) {
Text(`秒杀折扣:${(goods.discountRate * 10).toFixed(1)}折 | 剩余库存:${goods.seckillStock}件`)
.fontSize(12)
.margin(2)
.fontColor('#999999');
Text(`秒杀时间:${this.formatDate(goods.promoStartTime)} - ${this.formatDate(goods.seckillEndTime)}`)
.fontSize(10)
.margin(2)
.fontColor('#ff6600');
}
// Flex布局(API12语法)
Flex({ space: { main: LengthMetrics.vp(10), cross: LengthMetrics.vp(10) }, justifyContent: FlexAlign.Center, wrap: FlexWrap.Wrap }) {
// 按钮1:扣减库存
if (goods instanceof PhysicalGoods) {
Button("扣减库存")
.fontSize(10)
.padding({ left: 12, right: 12, top: 5, bottom: 5 })
.backgroundColor(Color.Green)
.fontColor(Color.White)
.borderRadius(4)
.onClick(() => this.handleReduceStock(goods, 1));
}
// 按钮2:扣减秒杀库存
if (goods instanceof SeckillGoods) {
Button("扣减秒杀库存")
.fontSize(10)
.padding({ left: 12, right: 12, top: 5, bottom: 5 })
.backgroundColor(Color.Orange)
.fontColor(Color.White)
.borderRadius(4)
.onClick(() => this.handleReduceSeckillStock(goods, 1));
}
// 按钮3:立即购买
if (goods instanceof VirtualGoods) {
Button("购买Vip")
.fontSize(10)
.padding({ left: 12, right: 12, top: 5, bottom: 5 })
.backgroundColor(Color.Pink)
.fontColor(Color.White)
.borderRadius(4)
.onClick(() => this.handleBuyVirtualGoods(goods));
}
}.margin({ top: 10 })
}
.width('90%')
.padding(15)
.backgroundColor(Color.White)
.borderRadius(10)
.shadow({ radius: 5, color: '#cccccc', offsetX: 2, offsetY: 2 })
.margin(10);
})
}.width('100%')
}.layoutWeight(1)
.width('100%')
}
.width('100%')
.height('100%')
.backgroundColor('#f5f5f5')
.padding(10);
}
}
5.2 预览运行

六、抽象类 vs 接口:核心区别与选型原则
6.1 核心区别对比表
| 特性 | 抽象类(abstract class) | 接口(interface) | 电商场景示例 |
|---|---|---|---|
| 核心定位 | 定义“基类规范”(是不是) | 定义“能力规范”(有什么) | 所有商品都是AbstractGoods;商品有IReturnable能力 |
| 继承/实现 | 单继承 | 多实现 | 秒杀商品仅能继承AbstractGoods,可实现双接口 |
| 成员类型 | 支持普通/抽象方法、可设属性初始值 | 仅定义类型规范,无任何实现逻辑 | printBaseInfo有实现;checkReturnable仅声明 |
| 访问修饰符 | public/protected/private | 仅public | _basePrice为protected;returnDays为public |
| 构造函数 | 支持 | 不支持 | 抽象类初始化商品名称;接口无构造函数 |
| 运行时类型判断 | 支持instanceof | 通过instanceof判断实现类实例 | goods instanceof PhysicalGoods ✅ |
| 代码复用 | 强(可复用具体实现) | 无(仅复用类型契约) | 复用基础信息打印;统一方法签名 |
| 工程适配 | 基类层(model核心) | 能力层(capability目录) | 抽象类放model根目录;接口放capability子目录 |
6.2 选型原则
-
优先用抽象类:
- 定义“一类事物的共性基类”且需复用实现逻辑时;
- 需要私有/受保护成员、构造函数时;
- 子类需要重写方法并复用父类逻辑(通过
super调用)时; - 示例:商品基类AbstractGoods、用户基类AbstractUser。
-
优先用接口:
- 为类扩展多维度能力且仅需定义规范时;
- 突破单继承限制,实现多能力组合时;
- 作为方法参数/返回值,降低模块耦合时;
- 示例:IReturnable(可退换)、ISeckill(可秒杀)、IPayable(可支付)。
-
最佳实践:
- 抽象类定基类规范,接口扩灵活能力;
- 一个核心抽象类 + N个能力接口,适配复杂业务场景;
- 子类重写抽象类方法时,通过
super调用父类实现,保证代码复用。
七、常见问题解答
7.1 多接口方法名冲突如何处理?
子类只需实现一次,逻辑需同时兼容所有接口规范,示例:
interface IA { doAction(): void }
interface IB { doAction(): void }
class C implements IA, IB {
doAction(): void {
console.log("统一处理IA/IB接口的doAction逻辑,兼容双接口规范");
}
}
7.2 接口方法声明中能否引用类的属性?
接口仅做类型声明,无法直接访问类的属性,但类实现接口方法时,可在方法体内访问自身或继承的属性(如SeckillGoods的checkPromoTimeValid方法访问this.name)。
7.3 ArkTS 核心语法约束
- 支持
instanceof(类实例类型判断)、typeof(基础类型判断); - 不支持
is类型谓词、in类型守卫,禁止使用&实现接口交叉; - 强依赖静态类型检查,禁止动态类型操作(如动态访问对象属性);
- 类型别名(type)仅在基础类型别名场景有限支持,优先用接口定义对象类型。
八、课堂小结
- 接口是“能力契约”,通过
interface定义、implements实现,可突破ArkTS单继承限制,是鸿蒙开发中扩展类能力的核心手段; - 接口支持可选属性、接口继承,能灵活适配电商场景的多维度能力扩展需求;
- 面向接口编程通过“接口作为参数/返回值+instanceof类实例判断”降低代码耦合,同时可复用已有实现逻辑;
- 抽象类与接口的选型核心是“是不是”(抽象类)和“有没有”(接口)的区别,子类可通过
super调用父类实现保证代码复用; - ArkTS核心约束:支持
instanceof/typeof、禁用is/in/&、禁止动态类型操作,接口为对象类型定义的首选; - 鸿蒙电商场景中,抽象类定义商品基类,接口扩展秒杀/退换能力,实现“基类稳定+能力灵活”的高内聚低耦合架构。
九、仓库代码
工程名称:ClassObjectDemo_3
代码地址:https://gitee.com/juhetianxia321/harmony-os-code-base.git
十、下节预告
下一节将聚焦泛型与类的灵活适配,掌握ArkTS泛型类/方法语法、泛型约束规则,开发通用商品容器与筛选工具,整合“抽象类+接口+泛型”三层架构,打通鸿蒙电商商品模块的工程化链路。
十一、鸿蒙开发者学习与认证指引
(一)官方学习班级报名(免费)
- 班级链接:HarmonyOS赋能资源丰富度建设(第四期)
- 学号填写规则:填写个人手机号码即可完成班级信息登记
(二)HarmonyOS应用开发者认证考试(免费)
- 考试链接:HarmonyOS开发者能力认证入口
- 认证等级及适配人群
- 基础认证:适配软件工程师、移动应用开发人员,需掌握HarmonyOS基础概念、DevEco Studio基础使用、ArkTS及ArkUI基础开发等能力;
- 高级认证:适配项目经理、工程架构师,需掌握系统核心技术理念、应用架构设计、关键技术开发及应用上架运维等能力;
- 专家认证:适配研发经理、解决方案专家,需掌握分布式技术原理、端云一体化开发、跨端迁移及性能优化等高级能力。
- 认证权益:通过认证可获得电子版证书以及其他专属权益。
浙公网安备 33010602011771号