散修带你入门鸿蒙应用开发基础第十五节:泛型与类的灵活适配
ArkTS基础第十五节:泛型与类的灵活适配
炼气十五重天
【学习目标】
- 理解泛型的核心价值(类型参数化,解决多类型商品管理的代码冗余问题),掌握ArkTS中泛型类、泛型方法的定义与调用语法;
- 掌握泛型约束(
extends关键字限定类型范围)的使用规则,结合上一节节的抽象商品类保障类型安全; - 实现泛型与抽象类、接口的联动开发,打造通用的商品管理工具(列表容器、筛选/排序工具);
- 基于鸿蒙电商场景落地泛型技术,完成商品库存排序、库存筛选的UI交互实战;
- 整合“抽象类+接口+泛型”三层架构,搭建高复用、高灵活度的商品管理体系。
【学习重点】
- 泛型核心概念:泛型参数
T的本质(类型占位符)、命名规范与作用范围; - 泛型基础语法:泛型类(带约束)、静态泛型方法的定义与调用;
- 泛型约束:通过
<T extends AbstractGoods>限定泛型仅适用于商品类及其子类; - 架构整合:泛型层与抽象类层、接口层的组合使用逻辑;
- 鸿蒙实战:基于泛型工具实现商品列表的统一管理、筛选与排序,并完成UI交互。
一、工程结构
基于上一节节的ClassObjectDemo_3工程,复制并重命名为ClassObjectDemo_4,新增泛型工具相关文件,核心结构如下:
ClassObjectDemo_4/
├── entry/ # 应用主模块
│ ├── src/main/ets/
│ │ ├── pages/
│ │ │ └── Index.ets # 测试页面(泛型工具实战+UI交互,基于原有内容扩展)
│ │ └── model/
│ │ ├── AbstractGoods.ets # 抽象父类:商品基类(原有内容,无修改)
│ │ ├── capability/ # 能力接口目录(原有内容,新增泛型相关文件)
│ │ │ ├── IReturnable.ets # 可退换能力接口(原有内容)
│ │ │ ├── IPromotion.ets # 基础促销能力接口(原有内容)
│ │ │ ├── ISeckill.ets # 可秒杀能力接口(原有内容)
│ │ │ ├── IGoodsComparator.ets # 新增:通用商品比较器泛型接口
│ │ │ └── GoodsComparators.ets # 新增:库存比较器实现+库存工具类
│ │ ├── PhysicalGoods.ets # 实体商品类(原有内容,新增统一库存方法)
│ │ ├── VirtualGoods.ets # 虚拟商品类(原有内容,无修改)
│ │ ├── SeckillGoods.ets # 秒杀商品类(原有内容,新增统一库存方法)
│ │ └── generic/ # 新增:泛型工具目录
│ │ ├── GoodsList.ets # 泛型商品列表容器(统一管理多类型商品)
│ │ └── GoodsTool.ets # 泛型商品工具类(筛选/排序功能)
│ ├── resources/ # 资源目录(复用)
│ └── module.json5 # 模块配置(API12适配,复用)
└── hvigorfile.ts # 构建脚本(复用)
二、为什么需要泛型?解决类型固化的痛点
在上一节中我们通过抽象类+接口实现了多类型商品的定义,但在管理这些商品时,出现了类型固化和代码冗余的问题:
// 问题1:每种商品都需要单独的列表类,代码重复率极高
class PhysicalGoodsList {
private list: PhysicalGoods[] = [];
add(goods: PhysicalGoods) { this.list.push(goods); }
getList() { return this.list; }
}
class SeckillGoodsList {
private list: SeckillGoods[] = [];
add(goods: SeckillGoods) { this.list.push(goods); }
getList() { return this.list; }
}
// 问题2:无法统一管理多类型商品,调用方需要针对不同列表编写不同逻辑
// 问题3:若新增商品类型,需重复编写相似的列表类,维护成本高
泛型的核心价值是类型参数化:用T(Type的缩写,也可使用K/V/E等语义化命名)作为“类型占位符”,定义类/方法时不指定具体类型,使用时再动态赋值。这能解决上述问题:
- 一个泛型类适配所有商品类型:无需为每种商品编写单独的列表类;
- 保证类型安全:编译期校验类型,避免运行时出现类型错误;
- 无缝兼容原有体系:与抽象类、接口联动,复用已有商品逻辑。
三、泛型核心语法实现(基于原有商品体系)
3.1 泛型类:通用商品列表容器
基于上一节节的AbstractGoods抽象类,实现泛型商品列表,仅支持存储AbstractGoods及其子类(实体/虚拟/秒杀商品),保障类型安全:
// model/generic/GoodsList.ets
import { AbstractGoods } from '../AbstractGoods';
/**
* 通用商品列表泛型容器
* @template T - 泛型参数,限定为AbstractGoods及其子类(泛型约束)
* @default AbstractGoods - 泛型默认类型,简化调用
*/
export class GoodsList<T extends AbstractGoods> {
// 私有列表:内部维护,对外返回副本(避免外部直接修改)
private readonly _goodsList: T[] = [];
/**
* 添加单个商品(含空值校验与日志)
* @param goods 商品实例(实体/虚拟/秒杀商品)
*/
addGoods(goods: T): void {
if (!goods) {
console.warn("【GoodsList】添加商品失败:商品实例为空");
return;
}
this._goodsList.push(goods);
console.log(`【GoodsList】成功添加商品:${goods.name}`);
}
/**
* 批量添加商品(过滤空值)
* @param goodsArr 商品实例数组
*/
addGoodsBatch(goodsArr: T[]): void {
if (!Array.isArray(goodsArr) || goodsArr.length === 0) {
console.warn("【GoodsList】批量添加失败:商品数组为空");
return;
}
const validGoods = goodsArr.filter(item => item);
this._goodsList.push(...validGoods);
console.log(`【GoodsList】批量添加成功:共添加${validGoods.length}件商品`);
}
/**
* 获取商品列表(返回副本,保障数据安全)
* @returns 商品列表副本
*/
getGoodsList(): T[] {
return [...this._goodsList]; // 解构赋值返回副本,外部修改不影响内部
}
/**
* 根据商品名称查询商品(含日志提示)
* @param name 商品名称
* @returns 匹配的商品实例(无则返回undefined)
*/
getGoodsByName(name: string): T | undefined {
if (!name || name.trim() === "") {
console.warn("【GoodsList】查询失败:商品名称为空");
return undefined;
}
const result = this._goodsList.find(item => item.name === name.trim());
if (result) {
console.log(`【GoodsList】查询到商品:${name}`);
} else {
console.log(`【GoodsList】未查询到商品:${name}`);
}
return result;
}
/**
* 获取商品总数
* @returns 商品数量
*/
getCount(): number {
return this._goodsList.length;
}
/**
* _goodsList被声明为readonly数组,意味着数组的引用不可变,但数组内的元素可以修改。通过length = 0可以清空数组元素。
* 清空商品列表(局部清空,仅当前列表实例)
* 含日志提示,保障数据操作的可追溯性
*/
clear(): void {
if (this._goodsList.length === 0) {
console.warn("【GoodsList】清空失败:商品列表已为空");
return;
}
this._goodsList.length = 0; // 清空数组
console.log("【GoodsList】商品列表已清空");
}
}
3.2 泛型接口:通用商品比较器
定义泛型比较器接口,为商品的库存排序、价格排序提供规范:
// model/capability/IGoodsComparator.ets
import { AbstractGoods } from '../AbstractGoods';
/**
* 通用商品比较器泛型接口
* @template T - 商品类型,限定为AbstractGoods子类
*/
export interface IGoodsComparator<T extends AbstractGoods> {
/**
* 比较两个商品的方法
* @param a 商品A
* @param b 商品B
* @returns 比较结果(a < b 返回负数;a > b 返回正数;相等返回0)
*/
compare(a: T, b: T): number;
}
3.3 库存比较器实现(适配泛型接口)
基于泛型比较器接口,实现库存升序/降序比较器,并抽离公共库存工具类:
// model/capability/GoodsComparators.ets
import { AbstractGoods } from '../AbstractGoods';
import { PhysicalGoods } from '../PhysicalGoods';
import { SeckillGoods } from '../SeckillGoods';
import { IGoodsComparator } from './IGoodsComparator';
/**
* 库存工具类:统一提取不同商品的库存
*/
export class StockTool {
/**
* 提取商品库存(实体商品返回stock,秒杀商品返回seckillStock,虚拟商品返回0)
* @param goods 商品实例
* @returns 库存数值
*/
public static getStock(goods: AbstractGoods): number {
if (!goods) { // 补充空值校验
return 0;
}
if (goods instanceof PhysicalGoods) {
return goods.stock;
} else if (goods instanceof SeckillGoods) {
return goods.seckillStock;
}
return 0; // 虚拟商品无库存
}
}
/**
* 库存升序比较器(实现泛型比较器接口)
*/
export class StockAscComparator implements IGoodsComparator<AbstractGoods> {
compare(a: AbstractGoods, b: AbstractGoods): number {
const stockA = StockTool.getStock(a);
const stockB = StockTool.getStock(b);
return stockA - stockB; // 升序:a库存 - b库存
}
}
/**
* 库存降序比较器(实现泛型比较器接口)
*/
export class StockDescComparator implements IGoodsComparator<AbstractGoods> {
compare(a: AbstractGoods, b: AbstractGoods): number {
const stockA = StockTool.getStock(a);
const stockB = StockTool.getStock(b);
return stockB - stockA; // 降序:b库存 - a库存
}
}
3.4 泛型方法:通用商品工具类
实现静态泛型方法,支持商品的价格筛选、比较器排序、库存筛选(修复空值校验漏洞):
// model/generic/GoodsTool.ets
import { AbstractGoods } from '../AbstractGoods';
import { IGoodsComparator } from '../capability/IGoodsComparator';
import { StockTool } from '../capability/GoodsComparators';
/**
* 通用商品工具类(静态泛型方法)
*/
export class GoodsTool {
/**
* 价格筛选:筛选低于指定价格的商品(补充空值校验)
* @template T - 商品类型
* @param goodsList 商品列表
* @param maxPrice 最高价格阈值
* @returns 符合条件的商品列表
*/
static filterByPrice<T extends AbstractGoods>(goodsList: T[], maxPrice: number): T[] {
if (!Array.isArray(goodsList) || typeof maxPrice !== 'number') {
console.error("【GoodsTool】价格筛选失败:入参非法");
return [];
}
const validMaxPrice = Math.max(maxPrice, 0); // 价格非负处理
// 补充goods空值校验,避免调用方法时出现空指针错误
return goodsList.filter(goods => goods && goods.calculateSellingPrice() <= validMaxPrice);
}
/**
* 比较器排序:适配自定义排序规则(如库存排序,补充空值过滤)
* @template T - 商品类型
* @param goodsList 商品列表
* @param comparator 自定义比较器
* @returns 排序后的商品列表
*/
static sortByComparator<T extends AbstractGoods>(goodsList: T[], comparator: IGoodsComparator<T>): T[] {
if (!Array.isArray(goodsList) || !comparator?.compare) {
console.warn("【GoodsTool】排序失败:入参非法");
return [...goodsList];
}
// 过滤空值后再排序,避免比较时出现空指针错误
const validGoods = goodsList.filter(item => item);
return [...validGoods].sort((a, b) => comparator.compare(a, b));
}
/**
* 库存筛选:仅保留有库存的商品(补充空值校验)
* @template T - 商品类型
* @param goodsList 商品列表
* @returns 有库存的商品列表
*/
static filterInStock<T extends AbstractGoods>(goodsList: T[]): T[] {
if (!Array.isArray(goodsList)) {
console.warn("【GoodsTool】库存筛选失败:商品列表非数组");
return [];
}
// 补充goods空值校验
return goodsList.filter(goods => goods && StockTool.getStock(goods) > 0);
}
}
3.5 商品类轻量扩展(可选)
为实体商品和秒杀商品新增统一库存方法,让库存获取逻辑更内聚(不修改原有核心逻辑):
// PhysicalGoods.ets 新增方法
public getStockAmount(): number {
return this.stock;
}
// SeckillGoods.ets 新增方法
public getStockAmount(): number {
return this.seckillStock;
}
// 此时StockTool的getStock方法可优化为:
public static getStock(goods: AbstractGoods): number {
if (!goods) return 0; // 保留空值校验
if (goods instanceof PhysicalGoods || goods instanceof SeckillGoods) {
return goods.getStockAmount();
}
return 0;
}
四、泛型实战:Index页面扩展(UI交互)
基于上一节的Index页面,整合泛型工具,实现库存排序、筛选的UI交互(新增无库存标红逻辑),保留原有核心逻辑:
// pages/Index.ets
import { PhysicalGoods } from '../model/PhysicalGoods';
import { VirtualGoods } from '../model/VirtualGoods';
import { SeckillGoods } from '../model/SeckillGoods';
import { AbstractGoods } from '../model/AbstractGoods';
import { GoodsList } from '../model/generic/GoodsList';
import { GoodsTool } from '../model/generic/GoodsTool';
import { StockAscComparator, StockDescComparator } from '../model/capability/GoodsComparators';
import { LengthMetrics } from '@kit.ArkUI';
@Entry
@Component
struct Index {
@State goodsList: AbstractGoods[] = [];
private originalList: AbstractGoods[] = []; // 原始列表(用于重置)
// 页面初始化
aboutToAppear(): void {
this.initGoods();
}
// 初始化商品(使用泛型列表统一管理)
private initGoods(): void {
// 1. 实例化原有商品
const phone = new PhysicalGoods("鸿蒙Mate70手机", 6999, 200, 0.2, "智能手机", 0.12);
// 新增无库存
const emptyStockPhone = new PhysicalGoods("鸿蒙Mate60手机(无库存)", 5999, 0, 0.2, "智能手机", 0.12); // 新增无库存商品
const vip = new VirtualGoods("鸿蒙VIP会员", 99, 365, true, "虚拟服务", 0.2);
const watch = new SeckillGoods("鸿蒙智能手表GT5(秒杀款)", 1299, 50, 0.7, new Date(), new Date(Date.now() + 7200000), "智能穿戴", 0.1);
// 2. 使用泛型列表管理商品
const goodsList = new GoodsList<AbstractGoods>();
goodsList.addGoodsBatch([phone, emptyStockPhone, vip, watch]); // 加入无库存商品
this.goodsList = goodsList.getGoodsList();
this.originalList = goodsList.getGoodsList()
}
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 ? "✅ 通过" : "❌ 失败"}`);
if (goods instanceof PhysicalGoods) {
goods.reduceStock(2);
} else if (goods instanceof SeckillGoods) {
goods.reduceSeckillStock(2);
}
});
this.goodsList = [...this.goodsList]; // 触发UI刷新
}
// 新增:库存升序排序
private sortStockAsc(): void {
this.goodsList = GoodsTool.sortByComparator(this.goodsList, new StockAscComparator());
}
// 新增:库存降序排序
private sortStockDesc(): void {
this.goodsList = GoodsTool.sortByComparator(this.goodsList, new StockDescComparator());
}
// 新增:筛选有库存商品
private filterInStock(): void {
this.goodsList = GoodsTool.filterInStock(this.goodsList);
}
// 新增:重置列表
private resetList(): void {
this.goodsList = [...this.originalList];
}
// 日期格式化方法
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);
// 新增:泛型工具操作按钮组
Flex({ space: { main: LengthMetrics.vp(10),cross:LengthMetrics.vp(10) }, wrap: FlexWrap.Wrap, justifyContent: FlexAlign.Center }) {
Button("库存升序")
.backgroundColor("#007DFF")
.fontColor(Color.White)
.borderRadius(6)
.onClick(() => this.sortStockAsc());
Button("库存降序")
.backgroundColor("#007DFF")
.fontColor(Color.White)
.borderRadius(6)
.onClick(() => this.sortStockDesc());
Button("筛选有库存")
.backgroundColor("#36D399")
.fontColor(Color.White)
.borderRadius(6)
.onClick(() => this.filterInStock());
Button("重置列表")
.backgroundColor("#F87272")
.fontColor(Color.White)
.borderRadius(6)
.onClick(() => this.resetList());
Button("执行核心操作")
.backgroundColor(Color.Blue)
.fontColor(Color.White)
.borderRadius(6)
.onClick(() => this.callSubclassMethods());
}
.margin({ bottom: 15 });
// 商品列表展示(新增无库存标红)
Scroll() {
Column() {
ForEach(this.goodsList, (goods: AbstractGoods) => {
Column() {
Text(goods.name)
.fontSize(18)
.fontWeight(FontWeight.Medium);
Text(`分类:${goods.category} | 底价:¥${goods.basePrice} | 售价:¥${goods.calculateSellingPrice().toFixed(2)}`)
.fontSize(14)
.margin(2)
.fontColor("#666");
// 子类专属信息(新增无库存标红逻辑)
if (goods instanceof PhysicalGoods) {
Text(`库存:${goods.stock}件 | 重量:${goods.weight.toFixed(2)}kg`)
.fontSize(12)
.margin(2)
.fontColor(goods.stock === 0 ? "#ff0000" : "#999"); // 无库存标红
} else if (goods instanceof VirtualGoods) {
Text(`有效期:${goods.validDays}天 | 自动续费:${goods.isAutoRenew ? "开启" : "关闭"}`)
.fontSize(12)
.margin(2)
.fontColor("#999");
} else if (goods instanceof SeckillGoods) {
Text(`秒杀库存:${goods.seckillStock}件 | 折扣:${(goods.discountRate * 10).toFixed(1)}折`)
.fontSize(12)
.margin(2)
.fontColor(goods.seckillStock === 0 ? "#ff0000" : "#999"); // 无库存标红
Text(`秒杀时间:${this.formatDate(goods.promoStartTime)} - ${this.formatDate(goods.seckillEndTime)}`)
.fontSize(10)
.margin(2)
.fontColor("#ff6600");
}
// 子类专属按钮
Flex({ space: { main: LengthMetrics.vp(10) }, justifyContent: FlexAlign.Center }) {
if (goods instanceof PhysicalGoods) {
Button("扣减库存")
.fontSize(10)
.backgroundColor(Color.Green)
.fontColor(Color.White)
.borderRadius(4)
.enabled(goods.stock > 0) // 无库存时按钮禁用
.onClick(() => {
goods.reduceStock(1);
this.goodsList = [...this.goodsList];
});
} else if (goods instanceof SeckillGoods) {
Button("扣减秒杀库存")
.fontSize(10)
.backgroundColor(Color.Orange)
.fontColor(Color.White)
.borderRadius(4)
.enabled(goods.seckillStock > 0) // 无库存时按钮禁用
.onClick(() => {
goods.reduceSeckillStock(1);
this.goodsList = [...this.goodsList];
});
} else if (goods instanceof VirtualGoods) {
Button("购买VIP")
.fontSize(10)
.backgroundColor(Color.Pink)
.fontColor(Color.White)
.borderRadius(4)
.onClick(() => {
console.log(`${goods.name}购买成功!`);
});
}
}
.margin({ top: 10 });
}
.width("90%")
.padding(15)
.backgroundColor(Color.White)
.borderRadius(10)
.shadow({ radius: 5, color: "#ccc", offsetX: 2, offsetY: 2 })
.margin(10);
});
}
.width("100%");
}
.layoutWeight(1)
.width("100%");
}
.width("100%")
.height("100%")
.backgroundColor("#f5f5f5")
.padding(10);
}
}
运行预览

五、泛型与抽象类、接口的整合逻辑
graph LR
A[AbstractGoods(抽象类:商品基类)] -->|泛型约束| B[GoodsList(泛型类:商品列表)]
A -->|泛型约束| C[GoodsTool(泛型方法:商品工具)]
A -->|泛型约束| D[IGoodsComparator(泛型接口:比较器)]
D -->|实现| E[StockAscComparator(库存升序比较器)]
A -->|继承| F[PhysicalGoods(实体商品)]
A -->|继承| G[SeckillGoods(秒杀商品)]
F -->|实现| H[IReturnable(可退换接口)]
G -->|实现| H
G -->|实现| I[ISeckill(可秒杀接口)]
- 抽象类:作为所有商品的基类,定义共性属性和方法,是泛型约束的基础;
- 接口:定义商品的能力规范(可退换、可秒杀)和泛型比较器规范;
- 泛型:基于抽象类的约束,实现多类型商品的统一管理和通用工具开发;
- 工具类:抽离公共逻辑(如库存提取),降低代码冗余,提升可维护性。
六、【仓库代码】
工程名称:ClassObjectDemo_4
代码地址:https://gitee.com/juhetianxia321/harmony-os-code-base.git
七、课堂小结
- 泛型通过类型参数化解决了商品管理的类型固化问题,一套工具可适配所有商品子类,而泛型约束(
T extends AbstractGoods)是保障类型安全的核心,避免了类型滥用; - 泛型接口(
IGoodsComparator)定义通用比较规范,具体实现类(StockAscComparator/StockDescComparator)+公共工具类(StockTool)的组合,既减少代码冗余,又符合工程化规范;可选的统一方法命名优化(getStockAmount)能进一步提升代码的扩展性和可维护性; - 库存相关的业务逻辑通过UI交互直观体现:排序(按库存升序/降序)、筛选(仅显示有库存商品),且新增的无库存标红+按钮禁用逻辑让用户体验更优,同时补充空值校验修复了潜在的空指针漏洞;
- 泛型与抽象类、接口的组合是鸿蒙电商模块的最优架构,实现了“基础规范+能力扩展+通用管理+交互落地”的全链路支撑;
- 实战中需遵循ArkTS静态类型检查规范,利用
instanceof类型窄化特性编写代码,同时做好空值校验和入参合法性校验,保障代码的健壮性和可运行性。
八、下节预告
下一节静态成员与readonly——商品全局管控将深入学习:
static静态成员:掌握类级属性/方法的定义与调用规范,实现商品ID全局自增、全品类商品总数统计;readonly只读修饰符:理解编译期不可篡改规则,为商品ID、创建时间等核心字段添加只读约束,杜绝非法修改;- 单例模式:基于私有构造+静态getInstance实现全局唯一的商品管理器,统一管控跨模块商品数据;
- 架构整合:将静态成员/readonly与抽象类、接口、泛型深度结合,落地鸿蒙电商“全局管控+类型安全”的商品模块;
- 避坑指南:识别静态成员滥用导致的状态混乱问题,区分readonly与const的使用边界。
静态成员实现“类级共享”、readonly保障“核心字段安全”、单例模式实现“全局唯一管控”,三者结合是鸿蒙大型工程中商品模块稳定性的核心保障。
九、鸿蒙开发者学习与认证指引
(一)、官方学习班级报名(免费)
- 班级链接:HarmonyOS赋能资源丰富度建设(第四期)
- 学号填写规则:填写个人手机号码即可完成班级信息登记
(二)、HarmonyOS应用开发者认证考试(免费)
- 考试链接:HarmonyOS开发者能力认证入口
- 认证等级及适配人群
- 基础认证:适配软件工程师、移动应用开发人员,需掌握HarmonyOS基础概念、DevEco Studio基础使用、ArkTS及ArkUI基础开发等能力;
- 高级认证:适配项目经理、工程架构师,需掌握系统核心技术理念、应用架构设计、关键技术开发及应用上架运维等能力;
- 专家认证:适配研发经理、解决方案专家,需掌握分布式技术原理、端云一体化开发、跨端迁移及性能优化等高级能力。
- 认证权益:通过认证可获得电子版证书以及其他专属权益。
浙公网安备 33010602011771号