JAVA 泛型

文章目录

参考

  1. https://blog.csdn.net/weixin_45395059/article/details/126006369

泛型

泛型就是把类型作为参数。以public static <E> E get(E e)为例,e是方法参数,而E是类型参数,在调用时才能确定具体类型。

泛型的优点是:

  1. 泛型提供类型安全检测机制,无需代码中显示类型转换,提升安全性。
  2. 泛型显式指定对象类型,提升可读性。
    泛型有三种使用方法:泛型类/接口,泛型参数。

泛型类/接口

JDK的ArrayList就是泛型类。List<E>就是泛型接口。
E是类型参数,JDK常用的有E(element), K(key), V(value), T(type)

由于实例化(new ArrayList<String>();)后才能确定类型参数E = String,因此只有成员变量和成员方法可以使用类的类型参数E。静态字段不可以使用类型参数。静态方法可以使用自己的类型参数。

java 复制代码
public class ArrayList<E> implements List<E> {
    transient Object[] elementData; // 存储元素的`Object`数组
    private int size; // `ArrayList`存储元素的容量
    public boolean add(E e) {
        modCount++;
        add(e, elementData, size);
        return true;
    }
    // elementData是存放`e`的`Object`数组。
    private void add(E e, Object[] elementData, int s) {
        if (s == elementData.length)
            elementData = grow();
        elementData[s] = e;
        size = s + 1;
    }
}

泛型接口的实现类可以是ArrayList<E>这种不定类型参数。也可以是MyList这种指定接口的类型参数,实例方法的类型参数为接口指定的类型参数。

java 复制代码
public interface List<E> extends Collection<E> {
    List<E> subList(int fromIndex, int toIndex);
}

public class MyList implements List<String> {
	@Override
	public List<String> subList(int fromIndex, int toIndex) {}
}

泛型方法

java 复制代码
public static <K, V> V getValue(K key, V value) {}

方法的泛型参数<K, V>在修饰符和返回类型之间。调用时确定实际对象类型类.<String, Integer>getValue("3", 1)

类型擦除

java脚本的编译分为两个阶段,一阶段是源码(.java文件)编译为字节码(.class文件),二阶段是字节码编译为JVM使用的二进制机器码。为了兼容早起版本,Java泛型在一阶段被擦除为原始类型,字节码文件里没有泛型参数。

java 复制代码
    List<String> list = new ArrayList<>();
    System.out.println(list.getClass()); // class java.util.ArrayList

泛型通配符

由于泛型擦除,虽然NumberInteger的父类,但是ArrayList<Integer>ArrayList<Number>的类型都是ArrayList,没有继承关系。下面代码会报错Type mismatch: cannot convert from ArrayList<Integer> to List<Number>

java 复制代码
    List<Number> nList;
    nList = new ArrayList<Integer>();

为了扩大泛型参数的范围,引入泛型通配符概念。泛型通配符有3种:<?>, <? extends T>, <? super T>

上界通配符 <? extends T>

T表示泛型类型的上界,泛型参数可以是T及子类。因此下面代码不会报错。

java 复制代码
    List<? extends Number> nList;
    nList = new ArrayList<Integer>();

<? extends T>上界通配符修饰的变量只能读,不能写。这就是PECS(Producer Extends, Consumer Super)中的PE

不能写的原因是上界通配符无法确定实际类型,如果2次写的实际类型不一致,如下代码,那就违背泛型的初衷了。有个特例,可以写null,因为null是所有类型的子类。

可以读的原因是实际类型必定是T的子类,多态允许用T表示实际类型。

java 复制代码
    List<Integer> iList = new ArrayList<>();
    iList.add(3);
    List<? extends Number> nList = iList;
    Number i = nList.get(0); // 编译正确
	
	nList.add(Integer.valueOf(33)); // 编译错误

下界通配符 <? super T>

T表示泛型类型的下界,泛型参数可以是T及父类。

java 复制代码
    ? super T get(int index);
    void set(? super T);

下界通配符修饰的变量只能写不能读。这就是PECS(Producer Extends, Consumer Super)中的CS

不能读get()的原因是无法确定实际类型。由于Object是所有类的父类,因此特例:可以将元素赋值给Object对象。

能写(set)的原因是:T的子类型一定是<? super T>的子类型。

java 复制代码
    List<Integer> iList = new ArrayList<>();
    iList.add(3);
    List<? super Integer> nList = iList;
    nList.add(Integer.valueOf(33));

    Number n = nList.get(0); // 编译错误
    Object o = nList.get(0); // 编译正确

无限定通配符 <?>

java 复制代码
    ? get(int index);
    void set(?);

get()方法只能赋值给Object对象。而set方法不能被调用,是Object也不能调用。因此无限定通配符修饰的对象是不可变对象。

相关推荐
tekin28 分钟前
Go、Java、Python、C/C++、PHP、Rust 语言全方位对比分析
java·c++·golang·编程语言对比·python 语言·php 语言·编程适用场景
李长渊哦2 小时前
Java 虚拟机(JVM)方法区详解
java·开发语言·jvm
陌殇殇3 小时前
002 SpringCloudAlibaba整合 - Feign远程调用、Loadbalancer负载均衡
java·spring cloud·微服务
猎人everest3 小时前
SpringBoot应用开发入门
java·spring boot·后端
山猪打不过家猪6 小时前
ASP.NET Core Clean Architecture
java·数据库·asp.net
AllowM6 小时前
【LeetCode Hot100】除自身以外数组的乘积|左右乘积列表,Java实现!图解+代码,小白也能秒懂!
java·算法·leetcode
不会Hello World的小苗6 小时前
Java——列表(List)
java·python·list
二十七剑7 小时前
jvm中各个参数的理解
java·jvm
东阳马生架构8 小时前
JUC并发—9.并发安全集合四
java·juc并发·并发安全的集合
计算机小白一个9 小时前
蓝桥杯 Java B 组之岛屿数量、二叉树路径和(区分DFS与回溯)
java·数据结构·算法·蓝桥杯