Day 12 - 泛型与阶段三总结
目标:掌握ArkTS泛型编程,完成Phase 03 OOP知识总结
预计时间:2-2.5小时
前置知识:Day 09-11 所有OOP内容
第一部分:泛型函数(对标 C++ 函数模板)
1.1 为什么需要泛型
思考一个问题:如何编写一个返回数组最后一个元素的函数?
// 方案1:为每种类型写单独的函数
function getLastNumber(arr: number[]): number {
return arr[arr.length - 1];
}
function getLastString(arr: string[]): string {
return arr[arr.length - 1];
}
function getLastBoolean(arr: boolean[]): boolean {
return arr[arr.length - 1];
}
// 问题:代码重复,维护困难
C++ 的解决方案是模板:
template<typename T>
T getLast(std::vector<T>& arr) {
return arr.back();
}
ArkTS 的解决方案是泛型:
function getLast<T>(arr: T[]): T {
return arr[arr.length - 1];
}
1.2 基本泛型函数语法
泛型函数使用 <T> 声明类型参数,函数体内可以像使用普通类型一样使用 T:
// 泛型函数的基本语法
function identity<T>(arg: T): T {
return arg;
}
// 使用
let num: number = identity<number>(42);
let str: string = identity<string>("hello");
对比C++函数模板:
| 特性 | C++ | ArkTS |
|---|---|---|
| 语法 | template<typename T> |
<T> |
| 类型参数位置 | 函数前 | 函数名后 |
| 实例化 | 编译期 | 编译期 |
| 类型推断 | 支持 | 支持 |
// C++ 函数模板
template<typename T>
T identity(T arg) {
return arg;
}
// 使用
int num = identity<int>(42);
std::string str = identity<std::string>("hello");
1.3 类型推断
ArkTS 编译器可以根据传入的参数自动推断类型参数,通常不需要显式指定:
function swap<T>(a: T, b: T): T[] {
return [b, a];
}
// 类型推断:编译器自动推断 T 为 number
let result1 = swap(10, 20);
// 类型推断:编译器自动推断 T 为 string
let result2 = swap("hello", "world");
// 显式指定(当推断不明确时需要)
let result3 = swap<number>(100, 200);
C++ 对比:
template<typename T>
std::pair<T, T> swap(T a, T b) {
return {b, a};
}
// C++17 起支持 CTAD(类模板参数推导),函数模板一直支持推断
auto result1 = swap(10, 20); // T 推断为 int
auto result2 = swap(std::string("hello"), std::string("world"));
1.4 显式指定类型参数
某些情况下,编译器无法推断类型,需要显式指定:
function createArray<T>(length: number, value: T): T[] {
let arr: T[] = [];
for (let i: number = 0; i < length; i++) {
arr.push(value);
}
return arr;
}
// 传入 null,T 被推断为 null 类型,可能不是预期结果
let arr1 = createArray(3, null); // 类型为 null[]
// 显式指定 T 为 string
let strArray: string[] = createArray<string>(3, "default");
// 显式指定 T 为 number
let numArray: number[] = createArray<number>(5, 0);
1.5 多个泛型参数
泛型函数可以声明多个类型参数:
function pair<T, U>(first: T, second: U): [T, U] {
return [first, second];
}
// 使用
let p1 = pair<number, string>(1, "one");
let p2 = pair<string, boolean>("active", true);
// 类型推断
let p3 = pair(100, "hundred"); // T=number, U=string
对比C++:
template<typename T, typename U>
std::pair<T, U> makePair(T first, U second) {
return {first, second};
}
auto p1 = makePair(1, std::string("one"));
auto p2 = makePair(std::string("active"), true);
第二部分:泛型约束(对标 C++ concepts / SFINAE)
2.1 为什么需要约束
无约束的泛型可以传入任何类型,但有时候我们需要访问特定属性:
function logLength<T>(arg: T): void {
// 编译错误:T 可能没有 length 属性
// console.log(arg.length.toString());
}
// 调用时可能传入没有 length 的类型
logLength(42); // number 没有 length
logLength(true); // boolean 没有 length
问题:如何让泛型只接受有 length 属性的类型?
C++ 的解决方案:
// C++20 concepts
template<typename T>
concept HasLength = requires(T t) {
{ t.length } -> std::convertible_to<int>;
};
template<HasLength T>
void logLength(T arg) {
std::cout << arg.length << std::endl;
}
// C++11/14/17 使用 SFINAE
template<typename T>
typename std::enable_if<has_length<T>::value, void>::type
logLength(T arg) { ... }
2.2 extends 约束
ArkTS 使用 extends 关键字约束泛型参数必须满足特定接口:
// 定义约束接口
interface HasLength {
length: number;
}
// 泛型约束:T 必须实现 HasLength 接口
function logLength<T extends HasLength>(arg: T): void {
console.log(arg.length.toString()); // 现在可以安全访问 length
}
// 使用
logLength("hello"); // string 有 length
logLength([1, 2, 3]); // 数组有 length
logLength({ length: 10 }); // 对象有 length 属性
// logLength(42); // 编译错误:number 不满足约束
// logLength(true); // 编译错误:boolean 不满足约束
对比C++ concepts:
| 特性 | C++20 | ArkTS |
|---|---|---|
| 约束定义 | concept |
interface |
| 应用约束 | template<Concept T> |
<T extends Interface> |
| 检查时机 | 编译期 | 编译期 |
2.3 使用 interface 定义约束
泛型约束通常与 interface 配合使用:
// 定义可比较接口
interface Comparable {
compareTo(other: Comparable): number;
}
// 约束:T 必须是可比较的
function findMax<T extends Comparable>(items: T[]): T {
if (items.length === 0) {
throw new Error("空数组");
}
let max: T = items[0];
for (let i: number = 1; i < items.length; i++) {
if (items[i].compareTo(max) > 0) {
max = items[i];
}
}
return max;
}
// 实现 Comparable 的类
class Device implements Comparable {
id: number;
name: string;
constructor(id: number, name: string) {
this.id = id;
this.name = name;
}
compareTo(other: Comparable): number {
// 类型断言,因为我们知道 other 也是 Device
let otherDevice = other as Device;
return this.id - otherDevice.id;
}
}
// 使用
let devices: Device[] = [
new Device(100, "设备A"),
new Device(200, "设备B"),
new Device(50, "设备C")
];
let maxDevice = findMax(devices);
console.log(maxDevice.name); // "设备B"
2.4 多重约束的实现
重要:ArkTS 不支持交叉类型(A & B),也不支持 <T extends A & B> 这样的语法。
如果需要多重约束,应该通过接口继承来实现:
// 定义基础约束接口
interface Identifiable {
id: number;
}
interface Named {
name: string;
}
// 通过接口继承合并约束
interface IdentifiableAndNamed extends Identifiable, Named {
// 继承了两个接口的所有属性
}
// 使用合并后的接口作为约束
function process<T extends IdentifiableAndNamed>(item: T): string {
return `ID: ${item.id}, Name: ${item.name}`;
}
// 实现类
class Product implements IdentifiableAndNamed {
id: number;
name: string;
price: number;
constructor(id: number, name: string, price: number) {
this.id = id;
this.name = name;
this.price = price;
}
}
// 使用
let product = new Product(1, "手机", 2999);
console.log(process(product));
更复杂的约束层次:
// 基础能力接口
interface Loggable {
log(): void;
}
interface Serializable {
serialize(): string;
}
interface Validatable {
validate(): boolean;
}
// 合并多个约束
interface Entity extends Identifiable, Named, Loggable, Serializable {
createdAt: number;
}
// 使用
function saveEntity<T extends Entity>(entity: T): void {
if (entity.validate === undefined || entity.validate()) {
let data: string = entity.serialize();
console.log(`保存 ${entity.name}(${entity.id}): ${data}`);
entity.log();
}
}
第三部分:泛型类(对标 C++ 类模板)
3.1 基本泛型类语法
泛型类在类名后使用 <T> 声明类型参数:
// 泛型类:类似 C++ 的 template<class T> class Box {}
class Box<T> {
private content: T;
constructor(value: T) {
this.content = value;
}
getContent(): T {
return this.content;
}
setContent(value: T): void {
this.content = value;
}
}
// 使用
let numberBox = new Box<number>(42);
console.log(numberBox.getContent().toString()); // "42"
let stringBox = new Box<string>("hello");
console.log(stringBox.getContent()); // "hello"
对比C++类模板:
template<typename T>
class Box {
private:
T content;
public:
Box(T value) : content(value) {}
T getContent() { return content; }
void setContent(T value) { content = value; }
};
// 使用
Box<int> numberBox(42);
Box<std::string> stringBox("hello");
3.2 泛型类实例化
// 显式指定类型参数
let intBox = new Box<number>(100);
let strBox = new Box<string>("text");
// 类型推断(从构造函数参数推断)
let inferredBox = new Box(3.14); // 推断为 Box<number>
// 复杂类型
interface Device {
id: number;
name: string;
}
let deviceBox = new Box<Device>({ id: 1, name: "传感器" });
console.log(deviceBox.getContent().name);
3.3 泛型属性和方法
class GenericStack<T> {
private items: T[] = [];
private capacity: number;
constructor(capacity: number) {
this.capacity = capacity;
}
// 泛型方法使用类的类型参数
push(item: T): boolean {
if (this.items.length >= this.capacity) {
console.log("栈已满");
return false;
}
this.items.push(item);
return true;
}
pop(): T {
if (this.items.length === 0) {
throw new Error("栈为空");
}
return this.items.pop() as T;
}
peek(): T {
if (this.items.length === 0) {
throw new Error("栈为空");
}
return this.items[this.items.length - 1];
}
isEmpty(): boolean {
return this.items.length === 0;
}
size(): number {
return this.items.length;
}
}
// 使用
let numStack = new GenericStack<number>(5);
numStack.push(10);
numStack.push(20);
numStack.push(30);
console.log(numStack.pop().toString()); // "30"
let strStack = new GenericStack<string>(3);
strStack.push("first");
strStack.push("second");
console.log(strStack.peek()); // "second"
对比C++:
template<typename T>
class Stack {
private:
std::vector<T> items;
size_t capacity;
public:
Stack(size_t cap) : capacity(cap) {}
bool push(const T& item) {
if (items.size() >= capacity) return false;
items.push_back(item);
return true;
}
T pop() {
if (items.empty()) throw std::runtime_error("Empty");
T item = items.back();
items.pop_back();
return item;
}
// ...
};
第四部分:泛型接口
4.1 泛型接口定义
接口也可以使用泛型:
// 泛型接口
interface Container<T> {
add(item: T): void;
remove(): T;
getSize(): number;
isEmpty(): boolean;
}
// 类实现泛型接口(指定具体类型)
class NumberContainer implements Container<number> {
private items: number[] = [];
add(item: number): void {
this.items.push(item);
}
remove(): number {
if (this.items.length === 0) {
throw new Error("容器为空");
}
return this.items.pop() as number;
}
getSize(): number {
return this.items.length;
}
isEmpty(): boolean {
return this.items.length === 0;
}
}
// 使用
let numContainer = new NumberContainer();
numContainer.add(100);
numContainer.add(200);
console.log(numContainer.remove().toString()); // "200"
4.2 类实现泛型接口(保持泛型)
类可以实现泛型接口并保持类型参数:
// 泛型接口
interface Repository<T> {
findById(id: number): T;
findAll(): T[];
save(item: T): void;
delete(id: number): boolean;
}
// 泛型类实现泛型接口
class MemoryRepository<T> implements Repository<T> {
private items: Map<number, T> = new Map();
private nextId: number = 1;
findById(id: number): T {
let item = this.items.get(id);
if (item === undefined) {
throw new Error(`未找到ID: ${id}`);
}
return item;
}
findAll(): T[] {
let result: T[] = [];
this.items.forEach((value: T, key: number) => {
result.push(value);
});
return result;
}
save(item: T): number {
let id = this.nextId++;
this.items.set(id, item);
return id;
}
delete(id: number): boolean {
return this.items.delete(id);
}
}
// 定义实体接口
interface User {
name: string;
email: string;
}
// 使用
let userRepo = new MemoryRepository<User>();
let userId = userRepo.save({ name: "张三", email: "zhangsan@example.com" });
let user = userRepo.findById(userId);
console.log(user.name); // "张三"
4.3 泛型接口的继承
泛型接口可以继承其他泛型接口:
// 基础泛型接口
interface Readable<T> {
read(): T;
}
interface Writable<T> {
write(item: T): void;
}
// 继承多个泛型接口
interface ReadWrite<T> extends Readable<T>, Writable<T> {
clear(): void;
}
// 实现
class Buffer<T> implements ReadWrite<T> {
private data: T[] = [];
read(): T {
if (this.data.length === 0) {
throw new Error("缓冲区为空");
}
return this.data.shift() as T;
}
write(item: T): void {
this.data.push(item);
}
clear(): void {
this.data = [];
}
}
// 使用
let buffer = new Buffer<string>();
buffer.write("message1");
buffer.write("message2");
console.log(buffer.read()); // "message1"
泛型接口继承时类型参数的独立性:
interface A<T> {
a: T;
}
interface B<U> {
b: U;
}
// 继承时可以独立指定类型参数
interface AB<T, U> extends A<T>, B<U> {
// 继承 A 的 a: T 和 B 的 b: U
}
class ABImpl<T, U> implements AB<T, U> {
a: T;
b: U;
constructor(a: T, b: U) {
this.a = a;
this.b = b;
}
}
// 使用
let a = new ABImpl(1, 2); // T=number, U=number
let b = new ABImpl(1, "2"); // T=number, U=string
第五部分:泛型默认参数与实用模式
5.1 泛型默认参数
泛型可以指定默认类型:
// 默认类型为 string
class Logger<T = string> {
private prefix: T;
constructor(prefix: T) {
this.prefix = prefix;
}
log(message: string): void {
console.log(`${this.prefix}: ${message}`);
}
}
// 使用默认类型(T = string)
let defaultLogger = new Logger("INFO");
defaultLogger.log("系统启动"); // "INFO: 系统启动"
// 显式指定类型
let numberLogger = new Logger<number>(1001);
numberLogger.log("设备连接"); // "1001: 设备连接"
泛型默认类型的真实作用:
-
简化复杂泛型的使用(减少需显式指定的类型参数数量)
// 多个类型参数时,后面的可以使用默认值 interface Container<T, U = T> { primary: T; secondary: U; } // 只指定一个类型,U 默认等于 T let c1: Container<number>; // U 默认为 number -
实现向后兼容(旧代码无需修改即可继续使用)
// 给已有类型添加泛型,但保持向后兼容 class Component<T = Object> { props: T; } // 旧的代码不用改,新的代码可以用泛型 let oldComponent = new Component(); // T = Object,兼容旧代码 let newComponent = new Component<string>(); // 新代码用泛型 -
降低API使用门槛(大部分场景只需指定关键类型)
// 复杂泛型,但大部分情况只需要指定一个 interface Query<T, ErrorType = Error> { data: T; error: ErrorType; } // 通常只指定 T 就够了 let q1: Query<User>;
重要澄清:
- 默认类型不会在类型推断失败时自动启用
- 默认类型是为了减少需要显式指定的类型参数数量
对比C++:
// C++ 模板默认参数
template<typename T = std::string>
class Logger {
T prefix;
public:
Logger(T p) : prefix(p) {}
void log(const std::string& msg) {
std::cout << prefix << ": " << msg << std::endl;
}
};
Logger<> defaultLogger("INFO");
Logger<int> numberLogger(1001);
5.2 泛型工厂模式
// 工厂接口
interface Factory<T> {
create(): T;
}
// 具体产品
class Device {
id: number;
name: string;
constructor(id: number, name: string) {
this.id = id;
this.name = name;
}
}
class Sensor {
type: string;
value: number;
constructor(type: string) {
this.type = type;
this.value = 0;
}
}
// 泛型工厂实现
class DeviceFactory implements Factory<Device> {
private nextId: number = 1;
create(): Device {
return new Device(this.nextId++, `设备-${this.nextId}`);
}
}
class SensorFactory implements Factory<Sensor> {
create(): Sensor {
return new Sensor("温度");
}
}
// 泛型工厂使用函数
function createProducts<T>(factory: Factory<T>, count: number): T[] {
let products: T[] = [];
for (let i: number = 0; i < count; i++) {
products.push(factory.create());
}
return products;
}
// 使用
let deviceFactory = new DeviceFactory();
let devices = createProducts(deviceFactory, 3);
console.log(`创建了 ${devices.length} 个设备`);
5.3 泛型仓库模式
// 实体接口
interface Entity {
id: number;
}
// 带约束的泛型仓库
class GenericRepository<T extends Entity> {
private items: Map<number, T> = new Map();
add(item: T): void {
this.items.set(item.id, item);
}
get(id: number): T {
let item = this.items.get(id);
if (item === undefined) {
throw new Error(`未找到: ${id}`);
}
return item;
}
getAll(): T[] {
let result: T[] = [];
this.items.forEach((value: T) => {
result.push(value);
});
return result;
}
update(item: T): void {
if (!this.items.has(item.id)) {
throw new Error(`不存在: ${item.id}`);
}
this.items.set(item.id, item);
}
delete(id: number): boolean {
return this.items.delete(id);
}
find(predicate: (item: T) => boolean): T[] {
let result: T[] = [];
this.items.forEach((value: T) => {
if (predicate(value)) {
result.push(value);
}
});
return result;
}
}
// 具体实体
class Product implements Entity {
id: number;
name: string;
price: number;
constructor(id: number, name: string, price: number) {
this.id = id;
this.name = name;
this.price = price;
}
}
// 使用
let productRepo = new GenericRepository<Product>();
productRepo.add(new Product(1, "手机", 2999));
productRepo.add(new Product(2, "平板", 3999));
productRepo.add(new Product(3, "耳机", 999));
// 查找价格大于1000的产品
let expensive = productRepo.find((p: Product) => p.price > 1000);
console.log(`高价商品: ${expensive.length} 个`);
Repository(仓库)命名说明:
Repository 是领域驱动设计(DDD)中的模式,用于封装对数据的访问逻辑。与 DAO(Data Access Object)相比,Repository 更面向领域,操作的是实体对象而非数据库表。
第六部分:阶段三总结
6.1 OOP知识全景图
Day 09: 类基础
├── 类定义 (class)
├── 属性与构造函数
├── 访问修饰符 (public/private/protected)
├── readonly 属性
├── Getter/Setter
├── 静态成员 (static)
Day 10: 继承与多态
├── extends 继承
├── super 关键字
├── 方法重写 (Override)
├── 多态 (Polymorphism)
├── instanceof 类型判断
└── 抽象类 (abstract)
Day 11: 接口与类型
├── interface 定义
├── implements 实现
├── 接口继承 (extends)
├── 多重接口继承
Day 12: 泛型
├── 泛型函数
├── 泛型约束 (extends)
├── 泛型类
├── 泛型接口
└── 泛型实用模式
6.2 关键概念对比表
Class vs Interface vs Abstract Class
| 特性 | Class | Interface | Abstract Class |
|---|---|---|---|
| 实例化 | ✅ 可以 | ❌ 不可以 | ❌ 不可以 |
| 实现代码 | ✅ 可以 | ❌ 不可以 | ✅ 可以(部分) |
| 抽象成员 | ❌ 不可以 | ✅ 必须抽象 | ✅ 可以有 |
| 继承 | extends(单继承) | extends(多继承) | extends(单继承) |
| 实现 | 被 implements | implements | 被 extends |
| 运行时 | 存在 | 擦除 | 存在 |
| 用途 | 创建对象 | 定义契约 | 基类模板 |
ArkTS vs C++ OOP 完整对比
| 概念 | C++ | ArkTS |
|---|---|---|
| 类定义 | class Name {} |
class Name {} |
| 构造函数 | Name(params) |
constructor(params) |
| 析构函数 | ~Name() |
自动垃圾回收 |
| 继承 | : public Base |
extends Base |
| 多继承 | ✅ 支持 | ❌ 不支持(类) |
| 接口多继承 | ❌ 不支持 | ✅ 支持 |
| 访问修饰符 | public:/private:/protected: |
每个成员单独标记 |
| 虚函数 | virtual |
自动虚函数 |
| 纯虚函数 | virtual void f() = 0 |
abstract method() |
| 抽象类 | 含纯虚函数的类 | abstract class |
| 静态成员 | static |
static |
| const成员 | const |
readonly |
| 函数模板 | template<typename T> |
<T> 泛型 |
| 类模板 | template<class T> |
class Name<T> |
| 模板约束 | C++20 concepts | extends Interface |
| 类型推断 | auto / CTAD |
自动推断 |
6.3 从C/C++到ArkTS的OOP差异总结
1. 内存管理
// C++:手动管理内存
Device* d = new Device();
// ... 使用 d ...
delete d; // 必须手动释放
// ArkTS:自动垃圾回收
let d = new Device();
// ... 使用 d ...
// 无需手动释放,垃圾回收器自动处理
2. 继承模型
// C++:多继承
class D : public A, public B, public C {}; // ✅ 允许
// ArkTS:类单继承,接口多继承
class D extends A implements B, C {
} // ✅ 类单继承+接口多实现
interface D extends A, B, C {} // ✅ 接口多继承
3. 虚函数机制
// C++:需要显式声明 virtual
class Base {
public:
virtual void method(); // 虚函数
void nonVirtual(); // 非虚函数
};
// ArkTS:所有方法都是"虚函数"
class Base {
method(): void {
} // 自动可被重写
readonly finalMethod = (): void => {
} // 类似 final
}
4. 泛型/模板
// C++:模板元编程强大但复杂
template<typename T>
class Container {
T* data;
public:
Container() : data(new T[100]) {}
~Container() { delete[] data; }
};
// ArkTS:泛型更简单,类型擦除
class Container<T> {
private data: T[] = []; // 直接使用数组
// 无需手动管理内存
}
5. 类型系统
| C++ | ArkTS |
|---|---|
| 编译期强类型 | 编译期强类型 |
| 模板实例化生成代码 | 泛型类型擦除 |
| 头文件/源文件分离 | 单文件模块 |
| 指针/引用 | 引用类型(无指针运算) |
第七部分:综合练习
综合练习题
题1:泛型函数
实现一个泛型函数 reverseArray<T>,接收一个数组并返回反转后的新数组。
// 要求:
// 1. 使用泛型实现
// 2. 不修改原数组
// 3. 保持类型安全
// 测试用例:
let nums = [1, 2, 3, 4, 5];
let reversedNums = reverseArray(nums);
console.log(reversedNums); // [5, 4, 3, 2, 1]
let strs = ["a", "b", "c"];
let reversedStrs = reverseArray(strs);
console.log(reversedStrs); // ["c", "b", "a"]
参考答案
function reverseArray<T>(arr: T[]): T[] {
let result: T[] = [];
for (let i: number = arr.length - 1; i >= 0; i--) {
result.push(arr[i]);
}
return result;
}
题2:泛型约束
定义一个 Printable 接口(包含 toString(): string 方法),实现一个泛型函数 printItems<T>,要求传入的数组元素必须实现 Printable。
// 要求:
// 1. 定义 Printable 接口
// 2. 使用泛型约束
// 3. 打印每个元素的 toString() 结果
// 测试用例:
class Person implements Printable {
name: string;
constructor(name: string) {
this.name = name;
}
toString(): string {
return `Person(${this.name})`;
}
}
let people = [new Person("Alice"), new Person("Bob")];
printItems(people);
// 输出:
// Person(Alice)
// Person(Bob)
参考答案
interface Printable {
toString(): string;
}
function printItems<T extends Printable>(items: T[]): void {
for (let i: number = 0; i < items.length; i++) {
console.log(items[i].toString());
}
}
题3:泛型类
实现一个泛型队列 Queue<T>,支持以下操作:
enqueue(item: T): void- 入队dequeue(): T- 出队peek(): T- 查看队首isEmpty(): boolean- 是否为空size(): number- 获取大小
// 测试用例:
let queue = new Queue<number>();
queue.enqueue(10);
queue.enqueue(20);
queue.enqueue(30);
console.log(queue.dequeue()); // 10
console.log(queue.peek()); // 20
console.log(queue.size()); // 2
参考答案
class Queue<T> {
private items: T[] = [];
enqueue(item: T): void {
this.items.push(item);
}
dequeue(): T {
if (this.isEmpty()) {
throw new Error("队列为空");
}
return this.items.shift() as T;
}
peek(): T {
if (this.isEmpty()) {
throw new Error("队列为空");
}
return this.items[0];
}
isEmpty(): boolean {
return this.items.length === 0;
}
size(): number {
return this.items.length;
}
}
题4:泛型接口与实现
定义泛型接口 Cache<K, V>(键值对缓存),实现类 MemoryCache<K, V>。
// 接口要求:
// - set(key: K, value: V): void
// - get(key: K): V
// - has(key: K): boolean
// - delete(key: K): boolean
// - clear(): void
// 测试用例:
let cache = new MemoryCache<string, number>();
cache.set("a", 100);
cache.set("b", 200);
console.log(cache.get("a")); // 100
console.log(cache.has("b")); // true
cache.delete("a");
console.log(cache.has("a")); // false
参考答案
interface Cache<K, V> {
set(key: K, value: V): void;
get(key: K): V;
has(key: K): boolean;
delete(key: K): boolean;
clear(): void;
}
class MemoryCache<K, V> implements Cache<K, V> {
private data: Map<K, V> = new Map();
set(key: K, value: V): void {
this.data.set(key, value);
}
get(key: K): V {
let value = this.data.get(key);
if (value === undefined) {
throw new Error(`Key not found: ${key}`);
}
return value;
}
has(key: K): boolean {
return this.data.has(key);
}
delete(key: K): boolean {
return this.data.delete(key);
}
clear(): void {
this.data.clear();
}
}
题5:多重约束
定义两个接口 Named(有 name 属性)和 Aged(有 age 属性),创建一个合并接口 Person,实现泛型函数 greet<T>。
// 要求:
// 1. Named 接口:name: string
// 2. Aged 接口:age: number
// 3. Person 接口继承两者
// 4. greet<T extends Person> 输出问候语包含 name 和 age
// 测试用例:
class Student implements Person {
name: string;
age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
}
greet(new Student("小明", 20));
// 输出:你好,小明!今年20岁。
参考答案
interface Named {
name: string;
}
interface Aged {
age: number;
}
interface Person extends Named, Aged {}
function greet<T extends Person>(person: T): void {
console.log(`你好,${person.name}!今年${person.age}岁。`);
}
题6:类 + 接口 + 泛型综合
创建一个设备管理系统,要求:
- 定义接口
Device(id, name, turnOn(), turnOff()) - 定义抽象类
BaseDevice实现Device - 创建具体类
Light和Fan继承BaseDevice - 创建泛型类
DeviceManager<T extends Device>管理设备
参考答案
interface Device {
id: number;
name: string;
turnOn(): void;
turnOff(): void;
}
abstract class BaseDevice implements Device {
id: number;
name: string;
protected isOn: boolean = false;
constructor(id: number, name: string) {
this.id = id;
this.name = name;
}
abstract turnOn(): void;
abstract turnOff(): void;
getStatus(): string {
return this.isOn ? "开启" : "关闭";
}
}
class Light extends BaseDevice {
brightness: number = 100;
turnOn(): void {
this.isOn = true;
console.log(`${this.name} 灯已开启,亮度${this.brightness}%`);
}
turnOff(): void {
this.isOn = false;
console.log(`${this.name} 灯已关闭`);
}
}
class Fan extends BaseDevice {
speed: number = 1;
turnOn(): void {
this.isOn = true;
console.log(`${this.name} 风扇已开启,档位${this.speed}`);
}
turnOff(): void {
this.isOn = false;
console.log(`${this.name} 风扇已关闭`);
}
}
class DeviceManager<T extends Device> {
private devices: T[] = [];
add(device: T): void {
this.devices.push(device);
}
turnOnAll(): void {
for (let i: number = 0; i < this.devices.length; i++) {
this.devices[i].turnOn();
}
}
turnOffAll(): void {
for (let i: number = 0; i < this.devices.length; i++) {
this.devices[i].turnOff();
}
}
findById(id: number): T {
for (let i: number = 0; i < this.devices.length; i++) {
if (this.devices[i].id === id) {
return this.devices[i];
}
}
throw new Error(`未找到设备: ${id}`);
}
}
// 使用
let manager = new DeviceManager<BaseDevice>();
manager.add(new Light(1, "客厅灯"));
manager.add(new Fan(2, "吊扇"));
manager.turnOnAll();
题7:泛型工具函数
实现以下泛型工具函数:
pick<T, K>- 从对象 T 中选取指定属性 K 组成新对象omit<T, K>- 从对象 T 中排除指定属性 K 组成新对象
注意:由于 ArkTS 限制,使用简化版本,只处理单层属性。
参考答案
// 简化版本,使用 Record 和类型断言
function pick<T extends Record<string, Object>, K extends keyof T>(
obj: T,
keys: K[]
): Pick<T, K> {
let result: Record<string, Object> = {};
for (let i: number = 0; i < keys.length; i++) {
let key = keys[i];
result[key as string] = obj[key];
}
return result as Pick<T, K>;
}
// 使用示例
interface User {
id: number;
name: string;
email: string;
age: number;
}
let user: User = {
id: 1,
name: "张三",
email: "zs@example.com",
age: 25
};
let picked = pick(user, ["id", "name"]);
console.log(picked.id); // 1
console.log(picked.name); // "张三"
题8:泛型默认值
创建一个泛型类 Response<T = string>,表示 API 响应:
data: T- 响应数据code: number- 状态码message: string- 消息
实现默认类型为 string 的版本和指定类型的版本。
参考答案
class Response<T = string> {
data: T;
code: number;
message: string;
constructor(data: T, code: number = 200, message: string = "OK") {
this.data = data;
this.code = code;
this.message = message;
}
isSuccess(): boolean {
return this.code >= 200 && this.code < 300;
}
}
// 使用默认类型(string)
let strResponse = new Response("操作成功");
console.log(strResponse.data); // "操作成功"
// 指定类型
interface UserData {
id: number;
name: string;
}
let userResponse = new Response<UserData>({ id: 1, name: "张三" });
console.log(userResponse.data.name); // "张三"
题9:泛型与枚举
结合泛型和枚举,实现一个状态机管理器:
- 定义枚举
Status(Pending, Active, Completed, Failed) - 定义接口
Task(id, status, execute()) - 创建泛型类
StateMachine<T extends Task>管理任务状态转换
参考答案
enum Status {
Pending = "PENDING",
Active = "ACTIVE",
Completed = "COMPLETED",
Failed = "FAILED"
}
interface Task {
id: number;
name: string;
status: Status;
execute(): void;
}
class StateMachine<T extends Task> {
private task: T;
private history: Status[] = [];
constructor(task: T) {
this.task = task;
this.history.push(task.status);
}
transitionTo(newStatus: Status): boolean {
// 简单状态转换规则
let validTransitions: Record<string, Status[]> = {
[Status.Pending]: [Status.Active],
[Status.Active]: [Status.Completed, Status.Failed],
[Status.Completed]: [],
[Status.Failed]: [Status.Pending]
};
let allowed = validTransitions[this.task.status];
for (let i: number = 0; i < allowed.length; i++) {
if (allowed[i] === newStatus) {
this.task.status = newStatus;
this.history.push(newStatus);
return true;
}
}
return false;
}
getHistory(): Status[] {
return this.history;
}
getCurrentStatus(): Status {
return this.task.status;
}
}
// 具体任务类
class DownloadTask implements Task {
id: number;
name: string;
status: Status = Status.Pending;
url: string;
constructor(id: number, name: string, url: string) {
this.id = id;
this.name = name;
this.url = url;
}
execute(): void {
console.log(`开始下载: ${this.url}`);
}
}
// 使用
let download = new DownloadTask(1, "文件下载", "http://example.com/file.zip");
let sm = new StateMachine(download);
console.log(sm.transitionTo(Status.Active)); // true
console.log(sm.transitionTo(Status.Completed)); // true
console.log(sm.transitionTo(Status.Pending)); // false(Completed不能转Pending)
题10:综合设计
设计一个可扩展的插件系统:
- 定义接口
Plugin<T>(name, version, init(config: T), execute()) - 创建泛型类
PluginManager<T>管理插件 - 实现两个具体插件:日志插件(配置为日志级别)和数据插件(配置为数据源)
参考答案
// 插件接口
interface Plugin<T> {
name: string;
version: string;
config: T;
init(config: T): void;
execute(): void;
}
// 插件管理器
class PluginManager<T> {
private plugins: Plugin<T>[] = [];
register(plugin: Plugin<T>): void {
this.plugins.push(plugin);
console.log(`注册插件: ${plugin.name} v${plugin.version}`);
}
initAll(config: T): void {
for (let i: number = 0; i < this.plugins.length; i++) {
this.plugins[i].init(config);
}
}
executeAll(): void {
for (let i: number = 0; i < this.plugins.length; i++) {
this.plugins[i].execute();
}
}
}
// 日志插件配置
interface LogConfig {
level: string;
output: string;
}
class LogPlugin implements Plugin<LogConfig> {
name: string = "LogPlugin";
version: string = "1.0.0";
config: LogConfig = { level: "INFO", output: "console" };
init(config: LogConfig): void {
this.config = config;
console.log(`日志插件初始化,级别: ${config.level}`);
}
execute(): void {
console.log(`[${this.config.level}] 执行日志记录`);
}
}
// 数据插件配置
interface DataConfig {
source: string;
timeout: number;
}
class DataPlugin implements Plugin<DataConfig> {
name: string = "DataPlugin";
version: string = "1.0.0";
config: DataConfig = { source: "default", timeout: 5000 };
init(config: DataConfig): void {
this.config = config;
console.log(`数据插件初始化,源: ${config.source}`);
}
execute(): void {
console.log(`从 ${this.config.source} 获取数据`);
}
}
// 使用
let logManager = new PluginManager<LogConfig>();
logManager.register(new LogPlugin());
logManager.initAll({ level: "DEBUG", output: "file" });
logManager.executeAll();
let dataManager = new PluginManager<DataConfig>();
dataManager.register(new DataPlugin());
dataManager.initAll({ source: "database", timeout: 10000 });
dataManager.executeAll();

浙公网安备 33010602011771号