hello大家好啊,这里是鸿蒙开天组,今天我们来学习鸿蒙中的接口和泛型。
接口和泛型在面向对象编程中,是两个很常见的概念,而今天的学习呢,是不需要在编辑器中实现什么东西的,关键是要理解这些概念,一看到类似的东西就能反应过来是什么意思。
一、接口
基础篇中,也给大家科普过接口的定义,就是拿来定义类型的,然后咱们今天看看它的其他用法
1.接口继承
接口继承,字面含义,就是一个接口继承另一个接口,比如A接口继承B接口,既然是继承,那B中的属性、类型和方法,A中自然也会有,同时接口A还可以自定义自己的属性和类型,语法关键字是extends,最基本的代码结构如下:
interface 接口1{属性1:类型
}
interface 接口2 extends 接口1 {属性2:类型
}
我们再来根据基础篇的例子,定义一个程序猿接口:
interface Person {name: stringage: numberweight: number
}interface Programmer extends Person{
hair:string
}const KK:Programmer={
name: '卡卡'
age: 18
weight: 180
hair:'发量浓密'
}
从以上代码就可以看出继承的用处,假如我有十几个不同职业,而所有职业都有name、age、weight这三个属性,不继承的话就需要把这三个写上十几次;而使用了继承,即可实现代码复用,只写一次完事。
2.接口实现
接口实现通常指的是类实现接口的过程。这个过程涉及到定义一个类,使其遵循接口中声明的方法和属性,语法关键字是implements,最基本的代码结构如下:
interface 接口{属性:类型方法:方法类型
}class 类 implements 接口{// 必须实现 接口中定义的 属性、方法,否则会报错
}
接口实现和接口继承相似,当然也是为了代码复用而设计,只不过换成了类以及需要初始化,例子如:
interface ICat {name: stringMiao: () => void
}class Cat implements ICat {name: string = ''food: string = ''Miao() {}
}
二、泛型
看着很高端的名字,实际上,只要你理解了参数是什么意思,就很容易理解泛型,简单地说,泛型就是一个类型参数,它也是一个待定的参数,只不过它比较特殊,代替的是特定的类型而已。它的作用是,让函数的类型更加灵活,不受到写死类型的限制。
1.泛型定义
举一个最简单的例子,我们有一个函数,咱们传入什么就返回什么:
// 1. 字符串
function retStr(arg: string): string {return arg
}// 2. 数字
function retNum(arg: number): number {return arg
}// 3. 布尔
function retBoolean(arg: boolean): boolean {return arg
}
这几个函数实现的逻辑都是一样的,就只有传入和返回的类型不一样,如果还有很多其他类型的,那就要多写很多个相似的函数,那么能否只定义一个函数,实现一样的效果呢?答案是可以的,只要写成以下形式即可:
function ret<T>(arg: T): T {return arg
}
以上代码中,只是在函数名后面加了尖括号和T“<T>”,然后分别把参数和返回值的类型也相应换成了T而已,就搞定啦~
而其中最关键的是尖括号,在这里它是一个代表泛型的标记,固定写法;而T是泛型参数的名字,和以前学的形参相似,可以自行定义,比如你写成Q、Type都可以,有意义即可,建议首字母大写,常用的是T、Type;
2.泛型使用
当然,在咱们定义泛型函数的时候,可以只用泛型代替具体类型,而到了使用时,再把具体类型加上即可,如果编辑器能推断出类型,那么具体类型不写也是可以的,尝试对比一下代码:
// 1. 字符串
function ReturnStr(arg: string): string {return arg
}//2.通用类型
function ret<T>(arg: T): T {return arg
}//对比组1,一样的使用
ReturnStr('1')
ret('1')//对比组2,补充具体类型与否
ret<number>(1)
ret(1)
3.泛型约束
如果开发中不希望任意的类型都可以传递给 类型参数 ,就可以通过泛型约束来完成
基本语法:
interface 接口{属性:类型
}
function 函数<Type extends 接口>(){}// 后续使用函数时,传入的类型必须要有 接口中的属性
说白了就是T后面加个extends关键字,和需要限定的接口名即可,这个关键字我们上面才学过,用来继承接口,你也可以变相理解成继承接口类型:
interface IFood {food: string
}function logFood<T extends IFood>(param: T) {console.log('吃的挺好:', param.food)
}//logFood的调用方式1
const meal: IFood = { food: 'burger' };
logFood(meal)//logFood的调用方式2
logFood({ food: 'burger' } as IFood)
4.多个泛型参数
日常开发的时候,如果有需要可以添加多个 类型变量,只需要定义并使用 多个类型变量即可,比如下面的代码,在尖括号里用逗号隔开的T1和T2:
function funcA<T1, T2>(param1: T1, param2: T2) {console.log('参数 1', param1)console.log('参数 2', param2)
}funcA<string, number>('苹果', 12345)
funcA<string[], boolean[]>(['香蕉'], [false])
5.泛型接口
定义接口时结合泛型,那么这个接口就是 泛型接口,比如下面的:
interface IdFunc<Type> {id: (value: Type) => Typeids: () => Type[]
}let obj: IdFunc<string> = {id(value) { return value },ids() { return ['香蕉', '苹果', '西瓜'] }
}
6.泛型类
和泛型接口类似,如果定义类的时候结合泛型,那么这个类就是 泛型类。这些概念都是相似的,只要我们掌握了本质,其他的都是看一眼就知道是些啥玩意:
// 定义
class Person <T> {id: Tconstructor(id: T) {this.id = id}getId(): T {return this.id}
}// 使用
let p = new Person<number>(1000)
好了,今天的分享就到这里,感谢阅读,如果对你有那么一点点帮助,还请点个赞点个收藏,那都是鸿蒙开天组更新的动力!