Harmony 泛型 | 给我三分钟带你快速了解鸿蒙开发中泛型基本应用

761 阅读7分钟

本篇文章将对Harmony ArkTS 语言中的泛型应用进行介绍,本篇文章将会涉及下几点:

  1. 泛型和泛型函数
  2. 泛型约束
  3. 多个泛型参数
  4. 泛型接口
  5. 泛型类

1. 泛型和泛型函数

我们可以把泛型理解成让类型进行变化的一个作用,之前我们都是要先进行定义类型,才能使用,但泛型是可以让这个类型进行变化,不需要去定义它,你后面需要什么类型,我就可以变成一个你需要的类型,让我来一看例子来进行理解。

这里有三个函数,返回值都是 arg,但是大家注意到他们的类型是不一样的吗?

// 1. 字符串 
function identityStr(arg: string) {
  return [arg]
}
​
// 2. 数字
function identityNum(arg: number) {
  return [arg]
}
​
// 3. 布尔
function identityBoolean(arg: boolean) {
  return [arg]
}

现在如果有一个技术的封装,可以让他们的类型可以进行变化,那么我们就可以简化代码,三个类型变成一个,那么这就是泛型出现的意义了。

// 1. 参数类型
function 函数名<Type>(形参:Type){
}
​
// 2. 返回值类型
function 函数名<Type>(形参:Type):Type{
}

这个泛型<Type>你可以理解成是一个占位符类型,在这里他指的是一个泛型函数,他可以是任何类型,我么可以这样进行理解,这个占位符会和鸿蒙底层逻辑说:“兄弟,我这里占一个类型的位置,我是什么类型我自己也不知道,等后面用到我,我再告诉你我是什么类型。”

我们可以这样进行调用函数

// 函数定义
function identity<Type>(arg: Type) {
  return [arg]
}
​
identity<string>('123')
identity<number>(123)
identity<boolean>(false)
identity<string[]>(['1', '2', '3'])

在第一次调用函数时,类型参数 string 被传递给 identity 函数,所以返回的值也是 string。第二次类型参数 number 被传递给 identity 函数,所以返回的值也是 number。同理第三次,第四次,你传给我什么样的类型我就返回给你什么类型的值。

通过泛型的使用,让我们能得到,函数的类型可以不被定义为一种,可以是多样性的,这样以来可以避免代码的复写,还可使编写出来的函数更加的通用,灵活。

基本使用

泛型函数的基本使用,泛型可以用尖括号 <T> 来表示,让我们来看下面的实例:

//创建泛型函数
function getArr<T>(a: T) {
  return a
}
//使用泛型函数
getArr<string>('hello word')
//使用泛型函数,它可以自行推断
getArr('hello word')
getArr<number>(0)
getArr(0)

第一我们首先创建一个函数 getArr ,接着使用了泛型类型参数 T ,返回值可以是任何类型。使用函数时展示了如何使用泛型函数,以及逻辑底层帮我们自行推断我们的参数类型。

2. 泛型约束

如果开发中不希望任意的类型都可以传递给参数类型,就可以通过泛型约束来完成。这句话怎么理解呢,我们通过代码来进行解释。

interface 接口{
  属性:类型
}
function 函数<Type extends 接口>(){}
​
// 后续使用函数时,传入的类型必须要有 接口中全部的属性

先不看extends 后面的,首先这个函数使用了泛型类型参数T,他的类型可以是任何类型,你返回任何值我都可以接受,但是现在不可以了,extends这个接口类型,这个接口里面只有一个属性一个类型,那现在就是说你现在必须要传一个指定的属性和类型给我了,这样就实现了对泛型的约束。

基本使用

我们来看一下泛型约束是如何在代码中体现的:

interface ILengthwise {
  length: number
}
​
function identity<Type extends ILengthwise>(arr: Type) {
  console.log(arr.length.toString())
}
​
// 使用的时候 只要有 length 属性即可
identity([1, 2, 3, 4]) // 数组有 length 属性 正常运行
identity('1234') // 字符串也有 length 属性 正常运行
// identity(124) // 数值没有 length 属性 报错

我们还是一样创建一个有泛型类型参数的函数identity,它的泛型被ILengthwise这个接口进行了一个约束,这个接口里只有一个属性length,一个类型number。现在是什么意思呢,就是你在调用我函数时不能是任何类型了,必须是一个数字类型的长度属性。

我们接下来看这个函数调用,第一个有长度属性嘛?当然有啦,那现在说明这个函数调用成功了。我们再看第二个,也同样有长度属性,字符串也是有length属性。第三个只是一个数字类型,但是没有长度属性啊,所以在输出时肯定是报错的。

当我们需要对泛型进行约束的时候,先定义一个用来约束的接口interface,类型参数通过extends就可以实现对泛型的约束了,如果要加更多的约束,只需要把作为约束的接口,添加更多内容就好。

3. 多个泛型参数

在日常开发的时候,如果有需要可以添加多个 类型变量,只需要定义并使用 多个类型变量即可

举个例子给大家看一下这句话是什么意思:

function funcA<T, T2>(param1: T, param2: T2) {
  console.log('参数 1', param1)
  console.log('参数 2', param2)
}
​
funcA<string, number>('西兰花', 998)
funcA<string[], boolean[]>(['西兰花'], [false])

我们之前是泛型里只放一个T,现在这个泛型里有两个,一个是T,一个是T2。那么相对应你的参数就要有两个一个是param1,另一个是param2,类型分别是TT2。在我们调用这个函数funcA时,就要写两个泛型参数,你写什么样的泛型参数你函数里就要相对应的写什么样的类型。

4. 泛型接口

定义接口时结合泛型,那么这个接口就是 泛型接口

我们还是一样结合代码来理解:

interface 接口<Type>{
  // 内部使用Type
}

最开始写接口的时候就是interface加一个接口,现在加上一个泛型,那么这个时候它就表示为一个泛型接口

结合实例代码我们来进行分析:

interface IdFunc<Type> {
  id: (value: Type) => Type
  ids: () => Type[]
}
​
let obj: IdFunc<number> = {
  id(value) { return value },
  ids() { return [1, 3, 5] }
}

现在这个接口是一个泛型接口,里面写了两个函数,一个id里面的参数是value,类型为泛型,返回值也是泛型;另一个ids返回的是一个数组,对应的类型也是泛型,传什么样的类型,我就是什么类型的数组。

我们直接let一个obj让这个obj等于我们这个接口,现在这个泛型明确为number那么你返回的值也要是number类型。

来看第一个id直接return value,是合法的,因为这个value的类型是这个泛型,那么现在泛型为number,那返回值也一样是number类型。第二个idsreturn了一个数字数组类型,那也是符合我们的ids返回值的要求。

5. 泛型类

泛型类和泛型接口非常相似,泛型类就是如果定义类的时候结合泛型,那么这个类就是 泛型类

通俗易懂,直接上基本代码:

class 类名<Type>{
  // 内部可以使用 Type
}

我们之前在写class类的时候就直接声明一个类名就好了,现在加上一个泛型,那么这个时候它就表示为一个泛型类

结合代码我们来进行分析:

class Person <T> {
  id: T
​
  constructor(id: T) {
    this.id = id
  }
​
  getId(): T {
    return this.id
  }
}
​
// 使用
let p = new Person<number>(10)

这是一个泛型类,现在我顶一个属性id类型为T,构建函数constructor来接受这个属性还有类型,紧接着创建一个方法getId,返回这个属性和类型。我们实例化一个实例p,泛型为number,那我们直接传数字就可以跑通代码了。

总结

泛型在Harmony中提供了更灵活,可重用的代码编写方式,它可以用于定义函数、对类型的约束,接口以及类,让我们能够编写适用于不同类型的代码。

第一次写文章如果有错误的地方或者不严谨的地方,期待大家的一个指正,感谢大家阅读。如果大家喜欢或者对大家有一点帮助请点个赞,也是对我的一种激励和支持吧。