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

相关推荐
spencer_tseng23 分钟前
VideoConvertor.java ffmpeg.exe
java
○陈26 分钟前
js面试题|[2024-12-10]
java·前端·javascript
微服务技术分享39 分钟前
探店小程序:解锁商业新生态,定制未来
java·小程序·小程序搭建·探店小程序搭建
寒山李白42 分钟前
Nignx部署Java服务测试使用的Spring Boot项目Demo
java·开发语言·spring boot
狄加山6751 小时前
C语言(函数)
java·c语言·算法
程序猿进阶1 小时前
可视化逻辑表达式编辑器
java·spring boot·后端·面试·性能优化·架构·编辑器
E___V___E1 小时前
计算机组成原理复习
java·开发语言
武子康1 小时前
Java-23 深入浅出 MyBatis - 手写ORM框架4 框架优化 SqlSession DefaultSqlSession
java·开发语言·sql·mybatis·springboot
Matrix701 小时前
Java_实例变量和局部变量及this关键字详解
java·开发语言
神洛华1 小时前
Y3编辑器文档4:触发器1(对话、装备、特效、行为树、排行榜、不同步问题)
java·开发语言·编辑器