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

相关推荐
huan_199311 分钟前
通过docker构建一个java镜像
java·docker
岁忧20 分钟前
(LeetCode 面试经典 150 题) 82. 删除排序链表中的重复元素 II (链表)
java·c++·leetcode·链表·面试·go
_herbert1 小时前
MAVEN构建分离依赖JAR
java
野犬寒鸦1 小时前
Pipeline功能实现Redis批处理(项目批量查询点赞情况的应用)
java·服务器·数据库·redis·后端·缓存
꧁༺摩༒西༻꧂2 小时前
Spring Boot Actuator 监控功能的简介及禁用
java·数据库·spring boot
Java中文社群2 小时前
快看!百度提前批的面试难度,你能拿下吗?
java·后端·面试
丨千纸鹤丨2 小时前
Tomcat
java·tomcat
发发发发8882 小时前
leetcode 674.最长连续递增序列
java·数据结构·算法·leetcode·动态规划·最长连续递增序列
回忆是昨天里的海3 小时前
3.3.2_1栈在表达式求值中的应用(上)
java··后缀表达式·前缀表达式
雨绸缪3 小时前
为什么 Java 在 2025 年仍然值得学习:开发人员的 25 年历程
java·后端·掘金·金石计划