TS学习-泛型基础

1,介绍

泛型相当于是一个类型变量,有时无法预先知道具体的类型,可以用泛型来代替。

通常会附属于函数,类,接口,类型别名之上。

举例:

在定义函数时,有时会丢失一些类型信息(多个位置应该保持一致)。

function handleArray(arr: any[], n: number): any[] {
    const temp: any[] = arr.slice(n)
    return temp
}

handleArray([1,2,30], 2)
handleArray(['1', '2', '3'], 2)

当传递 number 类型数组时,上面代码中所有 any[] 都应该是 number[],string 数组同理。

泛型来解决:

function handleArray<T>(arr: T[], n: number): T[] {
    const temp: T[] = arr.slice(n)
    return temp
}

handleArray<number>([1,2,30], 2)
handleArray<string>(['1', '2', '3'], 2)

1,在函数中使用

使用方式:定义函数和调用函数时,都在函数名之后使用 < > 来定义泛型名称

function handleArray<T>(arr: T[]): T[] {
   // ...
}

handleArray<number>()
handleArray<string>()

泛型对函数来说:在调用函数时,告诉函数操作的具体类型,函数在执行时就能确定具体的类型。

特点

1,只有在调用时,才能确定泛型的具体类型。

2,一般情况下,TS会智能的根据传递的参数,推导出泛型的具体类型。

以上面的例子来说,即便调用时没有写具体的泛型类型,函数也会根据传递的参数智能推导:

在这里插入图片描述

如果无法完成推导,并且没有传递具体的类型,默认为 unknown

function handleArray<T>(arr: any[], n: number): T[] {
    const temp: T[] = arr.slice(n)
    return temp
}

// unknown[]
const res = handleArray([1,2,30], 2)

3,泛型也可以有默认值。

几乎用不到,因为泛型就是用来表示可能得任意类型。

function handleArray<T = number>(arr: T[]): T[] {
    // ...
}

2,在类型别名,接口中使用

看例子即可,实现一个 filter 函数。

// type callback = (n: number, i: number) => boolean;

// type callback<T> = (n: T, i: number) => boolean;

interface callback<T> {
    (n: T, i: number): boolean;
}

function filter<T>(arr: T[], callback: callback<T>): T[] {
    let temp: T[] = [];
    arr.forEach((n, i) => {
        if (callback(n, i)) {
            temp.push(arr[i]);
        }
    });
    return temp;
}

const res = filter([1, 2, 3, 4], (n, i) => n % 2 !== 0);
console.log(res);

3,在类中使用

类名上指定的泛型,可以传递到定义的属性和方法上。

type callback<T> = (item: T, index?: number, arr?: T[]) => boolean;

class ArrayHelper<T> {
	constructor(private arr: T[]) {}

	myFilter(callback: callback<T>): T[] {
		let tempArr: T[] = [];
		this.arr.forEach((item, index, arr) => {
			if (callback(item, index, arr)) {
				tempArr.push(item);
			}
		});
		return tempArr;
	}
}

const tempArr = new ArrayHelper([2, 3, 4, 5]);

const res = tempArr.myFilter((item) => item % 2 === 0);

console.log(res);

2,泛型约束

用于约束泛型的取值:必须得有类型约束(接口,类型别名)中的成员。

举例:泛型必须兼容 User 接口。

interface User {
    name: string;
}

function updateName<T extends User>(obj: T): T {
    obj.name = obj.name.toUpperCase();
    return obj;
}

const obj = {
    name: "lover",
    age: 19,
};

const newNameObj = updateName(obj);
console.log(newNameObj);

3,多泛型

函数的参数会有多个,所以泛型也会有多个。

举例,混合2个不同类型的数组。

function mixinArray<T, K>(arr1: T[], arr2: K[]): (T | K)[] {
    const temp: (T | K)[] = [];
    for (let i = 0; i < arr1.length; i++) {
        temp.push(arr1[i]);
        temp.push(arr2[i]);
    }
    return temp;
}

console.log(mixinArray([1, 2], ["11", "22"]));

4,举例实现 Map

这是 Map 的接口定义

interface Map<K, V> {
    clear(): void;
    /**
     * @returns true if an element in the Map existed and has been removed, or false if the element does not exist.
     */
    delete(key: K): boolean;
    /**
     * Executes a provided function once per each key/value pair in the Map, in insertion order.
     */
    forEach(callbackfn: (value: V, key: K, map: Map<K, V>) => void, thisArg?: any): void;
    /**
     * Returns a specified element from the Map object. If the value that is associated to the provided key is an object, then you will get a reference to that object and any change made to that object will effectively modify it inside the Map.
     * @returns Returns the element associated with the specified key. If no element is associated with the specified key, undefined is returned.
     */
    get(key: K): V | undefined;
    /**
     * @returns boolean indicating whether an element with the specified key exists or not.
     */
    has(key: K): boolean;
    /**
     * Adds a new element with a specified key and value to the Map. If an element with the same key already exists, the element will be updated.
     */
    set(key: K, value: V): this;
    /**
     * @returns the number of elements in the Map.
     */
    readonly size: number;
}

简单模拟实现:

class MyMap<K, V> {
    private keys: K[] = [];
    private values: V[] = [];

    get size() {
        return this.keys.length;
    }

    clear(): void {
        this.keys = [];
        this.values = [];
    }

    delete(key: K): boolean {
        const index = this.keys.indexOf(key);
        if (index !== -1) {
            this.keys.splice(index, 1);
            this.values.splice(index, 1);
            return true;
        } else {
            return false;
        }
    }

    forEach(callbackfn: (value: V, key: K, map: MyMap<K, V>) => void): void {
        this.keys.forEach((item, i) => {
            callbackfn(this.values[i], item, this);
        });
    }

    get(key: K): V | undefined {
        const index = this.keys.indexOf(key);
        return this.values[index];
    }

    has(key: K): boolean {
        return return this.keys.includes(key);
    }

    set(key: K, value: V): this {
        const index = this.keys.indexOf(key);
        if (index !== -1) {
            this.keys.push(key);
            this.values.push(value);
        } else {
            this.values[index] = value;
        }
        return this;
    }
}

const myMap = new MyMap<string, number>();

myMap.set("a", 1);
myMap.set("b", 2);
myMap.set("c", 3);
console.log(myMap.get("d"));
myMap.has("a");
console.log(myMap.delete("c"));

myMap.forEach((value, key, self) => {
    console.log(value, key, self);
});

myMap.clear();
console.log(myMap.size);

以上。

  • 10
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Java泛型是Java 5引入的新特性,可以提高代码的可读性和安全性,降低代码的耦合度。泛型是将类型参数化,实现代码的通用性。 一、泛型的基本语法 在声明类、接口、方法时可以使用泛型泛型的声明方式为在类名、接口名、方法名后面加上尖括号<>,括号中可以声明一个或多个类型参数,多个类型参数之间用逗号隔开。例如: ```java public class GenericClass<T> { private T data; public T getData() { return data; } public void setData(T data) { this.data = data; } } public interface GenericInterface<T> { T getData(); void setData(T data); } public <T> void genericMethod(T data) { System.out.println(data); } ``` 其中,`GenericClass`是一个泛型类,`GenericInterface`是一个泛型接口,`genericMethod`是一个泛型方法。在这些声明中,`<T>`就是类型参数,可以用任何字母代替。 二、泛型的使用 1. 泛型类的使用 在使用泛型类时,需要在类名后面加上尖括号<>,并在括号中指定具体的类型参数。例如: ```java GenericClass<String> gc = new GenericClass<>(); gc.setData("Hello World"); String data = gc.getData(); ``` 在这个例子中,`GenericClass`被声明为一个泛型类,`<String>`指定了具体的类型参数,即`data`字段的类型为`String`,`gc`对象被创建时没有指定类型参数,因为编译器可以根据上下文自动推断出类型参数为`String`。 2. 泛型接口的使用 在使用泛型接口时,也需要在接口名后面加上尖括号<>,并在括号中指定具体的类型参数。例如: ```java GenericInterface<String> gi = new GenericInterface<String>() { private String data; @Override public String getData() { return data; } @Override public void setData(String data) { this.data = data; } }; gi.setData("Hello World"); String data = gi.getData(); ``` 在这个例子中,`GenericInterface`被声明为一个泛型接口,`<String>`指定了具体的类型参数,匿名内部类实现了该接口,并使用`String`作为类型参数。 3. 泛型方法的使用 在使用泛型方法时,需要在方法名前面加上尖括号<>,并在括号中指定具体的类型参数。例如: ```java genericMethod("Hello World"); ``` 在这个例子中,`genericMethod`被声明为一个泛型方法,`<T>`指定了类型参数,`T data`表示一个类型为`T`的参数,调用时可以传入任何类型的参数。 三、泛型的通配符 有时候,我们不知道泛型的具体类型,可以使用通配符`?`。通配符可以作为类型参数出现在方法的参数类型或返回类型中,但不能用于声明泛型类或泛型接口。例如: ```java public void printList(List<?> list) { for (Object obj : list) { System.out.print(obj + " "); } } ``` 在这个例子中,`printList`方法的参数类型为`List<?>`,表示可以接受任何类型的`List`,无论是`List<String>`还是`List<Integer>`都可以。在方法内部,使用`Object`类型来遍历`List`中的元素。 四、泛型的继承 泛型类和泛型接口可以继承或实现其他泛型类或泛型接口,可以使用子类或实现类的类型参数来替换父类或接口的类型参数。例如: ```java public class SubGenericClass<T> extends GenericClass<T> {} public class SubGenericInterface<T> implements GenericInterface<T> { private T data; @Override public T getData() { return data; } @Override public void setData(T data) { this.data = data; } } ``` 在这个例子中,`SubGenericClass`继承了`GenericClass`,并使用了相同的类型参数`T`,`SubGenericInterface`实现了`GenericInterface`,也使用了相同的类型参数`T`。 五、泛型的限定 有时候,我们需要对泛型的类型参数进行限定,使其只能是某个类或接口的子类或实现类。可以使用`extends`关键字来限定类型参数的上限,或使用`super`关键字来限定类型参数的下限。例如: ```java public class GenericClass<T extends Number> { private T data; public T getData() { return data; } public void setData(T data) { this.data = data; } } public interface GenericInterface<T extends Comparable<T>> { T getData(); void setData(T data); } ``` 在这个例子中,`GenericClass`的类型参数`T`被限定为`Number`的子类,`GenericInterface`的类型参数`T`被限定为实现了`Comparable`接口的类。 六、泛型的擦除 在Java中,泛型信息只存在于代码编译阶段,在编译后的字节码中会被擦除。在运行时,无法获取泛型的具体类型。例如: ```java public void genericMethod(List<String> list) { System.out.println(list.getClass()); } ``` 在这个例子中,`list`的类型为`List<String>`,但是在运行时,`getClass`返回的类型为`java.util.ArrayList`,因为泛型信息已经被擦除了。 七、泛型的类型推断 在Java 7中,引入了钻石操作符<>,可以使用它来省略类型参数的声明。例如: ```java List<String> list = new ArrayList<>(); ``` 在这个例子中,`ArrayList`的类型参数可以被编译器自动推断为`String`。 八、总结 Java泛型是一个强大的特性,可以提高代码的可读性和安全性,降低代码的耦合度。在使用泛型时,需要注意它的基本语法、使用方法、通配符、继承、限定、擦除和类型推断等问题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

下雪天的夏风

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值