01-泛型

举报
kwan的解忧杂货铺 发表于 2024/05/27 22:12:24 2024/05/27
【摘要】 一.概念 1.什么是泛型泛型的本质是类型参数化。允许在定义类、接口、方法时使用类型形参,当使用时指定具体类型。所有使用该泛型参数的地方都被统一化,保证类型一致。如果未指定具体类型,默认是 Object 类型。集合体系中的所有类都增加了泛型,泛型也主要用在集合。泛型是将类型参数化,允许定义在类、接口、方法时使用类型参数,当使用的时候指定具体类型。泛型主要应用在集合 2.泛型的优点代码需要更精...

一.概念

1.什么是泛型

  • 泛型的本质是类型参数化。
  • 允许在定义类、接口、方法时使用类型形参,当使用时指定具体类型。
  • 所有使用该泛型参数的地方都被统一化,保证类型一致。
  • 如果未指定具体类型,默认是 Object 类型。
  • 集合体系中的所有类都增加了泛型,泛型也主要用在集合。

泛型是将类型参数化,允许定义在类、接口、方法时使用类型参数,当使用的时候指定具体类型。

泛型主要应用在集合

2.泛型的优点

  • 代码需要更精简
  • 程序更加健壮
  • 编码期,可读性很高

3.类型擦除和桥接方法

泛型是给 javac 编译器使用,在编译器编译之后的 class 文件中是没有泛型信息的,所以泛型的使用不会让程序运行效率收到影响,这个过程称之为擦除。
由于类型擦除了,为了维持多态性,需要一些桥接方法类保持多态性,桥接方法是编译器自动生成的

// 最初的代码
public class Node<T> {
	public T data;
	public void setData(T data) {
		this.data = data;
	}
}
public class MyNode extends Node<Integer> {
	public void setData(Integer data) {
		//....
	}
}
// jvm不认识泛型的,把泛型擦除掉, 兼容⽼版本jdk
public class Node {
  public Object data;
  public void setData(Object data) {
    this.data = data;
  }
}
public class MyNode extends Node {
  // 桥接⽅法,编译器⾃动⽣成
  public void setData(Object data) {
    setData((Integer)data)
  }
  public void setData(Integer data) {
    //....
  }
}

二.泛型上下限

1.泛型的上下限

上限
格式:==类型名称 <? extend 类> 对象名称==
说明:只能接收该类型及其子类,指的是参数,不是修改

下限
格式:==类型名称 <? super 类> 对象名称==
说明:只能接收该类型及其父类型,指的是参数是父类,添加是该类型自己

2.通配符的上限

/**
 * 测试类
 * 通配符的上限 List<? extends Type>
 * 格式:类型名称<? extends 类> 对象名称
 * 意义:只能接收该类型及其子类
 *
 * @author : kwan
 * @version : 1.0.0
 * @date : 2022/8/11 16:57
 */
public class Collection_02_extends_super {
    public static void main(String[] args) {
        List<Dog> dogs = new ArrayList<>();
        dogs.add(new Dog());

        // 编译器只知道animals里保存的是Animal的子类
        //但是并不知道是哪个子类
        //Animal 是接口 ,无法添加
        List<? extends Animal> animals = dogs;

//        animals.add(new Dog()); // 编译不通过
//        animals.add(new Animal()); // 编译不通过
//
        Animal animal = animals.get(0);
//        Dog dog = animals.get(0); // 编译不通过
    }
}

3.通配符的下限

/**
 * 测试类
 * 通配符的下限 List<? supper Type>
 * 格式:类型名称<? supper 类> 对象名称
 * 意义:只能接收该类型及其父类
 *
 * @author : kwan
 * @version : 1.0.0
 * @date : 2022/8/11 16:57
 */
public class Collection_03_extends_super {
  public static void main(String[] args) {
    List<Dog> as = new ArrayList<>();
    as.add(new Dog());
    // 编译器只知道bs里保存的是BigDog的父类
    List<? super BigDog> bs = as;
    //      bs.add(new Dog()); // 编译不通过
    bs.add(new BigDog());
    //      Dog a = bs.get(0); // 编译不通过
    //      BigDog b = bs.get(0); // 编译不通过
  }
}

4.上限解读

通配符的上限 List<? extends Type>可以接收该类型及其子类,即可以接收 Type 的所有子类的 List 对象。

使用通配符的上限可以使得我们在定义方法或变量时,可以限制传入的参数类型或赋值的对象类型。例如,我们定义了一个方法,需要传入一个 List 对象,但我们只需要使用 List 中的一部分元素,此时我们可以使用通配符的上限来限制传入的 List 类型,代码如下:

public class Collection_03_extends_super {
    public static void main(String[] args) {
        List<Dog> dogs = new ArrayList<>();
        dogs.add(new Dog());
        printList(dogs);
    }

    public static void printList(List<? extends Animal> list) {
        for (Animal n : list) {
            System.out.println(n);
        }
    }
}

在上述代码中,我们使用了通配符的上限 List<? extends Animal>来限制传入的 List 对象类型,可以接收 Animal 及其子类的 List 对象。在方法中,我们只需要遍历 List 中的元素,不需要对 List 进行修改,因此使用通配符的上限可以很好地满足我们的需求。

需要注意的是,通配符的上限只能用于读取操作,不能用于写入操作。即我们可以从 List 中读取元素,但不能向 List 中添加元素。这是因为,通配符的上限限制了传入的 List 对象类型,但不能确定 List 对象的具体类型,因此不能向 List 中添加元素。

5.下限解读

通配符的下限 List<? super Type>可以接收该类型及其父类,即可以接收 Type 的所有父类的 List 对象。

使用通配符的下限可以使得我们在定义方法或变量时,可以限制传入的参数类型或赋值的对象类型。通配符的下限通常用于写入操作,例如,我们定义了一个方法,需要向 List 中添加元素,但我们只能添加类型为 Type 及其父类的元素,此时我们可以使用通配符的下限来限制传入的 List 类型,代码如下:

public class Collection_05_extends_super {
    /**
     * BigDog继承Dog
     *
     * @param args
     */
    public static void main(String[] args) {
        List<Dog> as = new ArrayList<>();
        as.add(new Dog());
        addToList(as);
    }

    public static void addToList(List<? super BigDog> list) {
        list.add(new BigDog());
        list.add(new BigDog());
        list.add(new BigDog());
        for (Object o : list) {
            System.out.println(o);
        }
    }
}

在上述代码中,我们使用了通配符的下限 List<? super BigDog>来限制传入的 List 对象类型,可以接收 BigDog 及其父类的 List 对象。在方法中,我们向 List 中添加了三个元素,这些元素的类型都是 BigDog 及其子类,因此不会违反通配符的下限的限制。

需要注意的是,通配符的下限只能用于写入操作,不能用于读取操作。即我们可以向 List 中添加元素,但不能从 List 中读取元素。这是因为,通配符的下限限制了传入的 List 对象类型,但不能确定 List 对象的具体类型,因此不能从 List 中读取元素。

三.泛型符号

1.常用符号

本质上这些个都是通配符,没啥区别,只不过是编码时的一种约定俗成的东西。比如上述代码中的 T ,我们可以换成 A-Z 之间的任何一个 字母都可以,并不会影响程序的正常运行,但是如果换成其他的字母代替 T ,在可读性上可能会弱一些。通常情况下,T,E,K,V,?是这样约定的:

  • ?表示不确定的 java 类型
  • T (type) 表示具体的一个 java 类型
  • K V (key value) 分别代表 java 键值中的 Key Value
  • E (element) 代表 Element

2.?和 T 的区别

?和 T 都表示不确定的类型,区别在于我们可以对 T 进行操作,但是对 ?不行,比如如下这种

// 可以
T t = operate();

// 不可以
?car = operate();

T 是一个 确定的 类型,通常用于泛型类和泛型方法的定义,?是一个 不确定 的类型,通常用于泛型方法的调用代码和形参,不能用于定义类和泛型方法。

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。