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也不能调用。因此无限定通配符修饰的对象是不可变对象。

相关推荐
num_killer4 小时前
小白的Langchain学习
java·python·学习·langchain
期待のcode5 小时前
Java虚拟机的运行模式
java·开发语言·jvm
程序员老徐5 小时前
Tomcat源码分析三(Tomcat请求源码分析)
java·tomcat
a程序小傲5 小时前
京东Java面试被问:动态规划的状态压缩和优化技巧
java·开发语言·mysql·算法·adb·postgresql·深度优先
仙俊红5 小时前
spring的IoC(控制反转)面试题
java·后端·spring
阿湯哥5 小时前
AgentScope Java 集成 Spring AI Alibaba Workflow 完整指南
java·人工智能·spring
小楼v6 小时前
说说常见的限流算法及如何使用Redisson实现多机限流
java·后端·redisson·限流算法
与遨游于天地6 小时前
NIO的三个组件解决三个问题
java·后端·nio
czlczl200209256 小时前
Guava Cache 原理与实战
java·后端·spring
yangminlei6 小时前
Spring 事务探秘:核心机制与应用场景解析
java·spring boot