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

相关推荐
珹洺36 分钟前
C++从入门到实战(十)类和对象(最终部分)static成员,内部类,匿名对象与对象拷贝时的编译器优化详解
java·数据结构·c++·redis·后端·算法·链表
一 乐39 分钟前
网红酒店|基于java+vue的网红酒店预定系统(源码+数据库+文档)
java·开发语言·数据库·毕业设计·论文·springboot·网红酒店预定系统
xyliiiiiL3 小时前
从责任链模式聊到aware接口
java·开发语言
码农老起6 小时前
与Aspose.pdf类似的jar库分享
java·pdf·jar
程序猿小D6 小时前
第三百八十九节 JavaFX教程 - JavaFX WebEngine
java·eclipse·intellij-idea·vr·javafx
self-discipline6348 小时前
【Java】Java核心知识点与相应面试技巧(七)——类与对象(二)
java·开发语言·面试
wei3872452328 小时前
java笔记02
java·开发语言·笔记
zjj5878 小时前
Docker使用ubuntu
java·docker·eureka
士别三日&&当刮目相看8 小时前
JAVA学习*简单的代理模式
java·学习·代理模式
ShareBeHappy_Qin9 小时前
设计模式——设计模式理念
java·设计模式