Java泛型-概念

1. 什么是Java泛型?

Java泛型是JDK 5 中引入的一种编程语言特性,它允许在编写代码时使用参数化类型。泛型的主要目的是提高代码的灵活性和重用性,同时在编译时提供更强的类型检查。
泛型具有以下优点:

  • 编译时的强类型检查
  • 避免了类型转换
  • 泛型编程可以实现通用算法
1
2
3
4
5
6
7
8
9
// 未使用泛型
List list1 = new ArrayList();
list1.add(1);
Integer i1 = (Integer) list1.get(0);

// 使用泛型
List<String> list2 = new ArrayList<>();
list2.add("hello world");
String s2 = list2.get(0);

以上例子在未使用泛型的情况下,list1中存放的是Object类型,在从list1中获取i1时需要进行强制类型转换,如果在list1中不小心放入了String类型,程序在运行中会抛出异常;
在使用泛型后,list2中只能存放String类型,如果往list2中放入Integer类型,在编写和编译代码时就能依赖JVM的强类型检查尽早发现错误。

2. 泛型的基本使用

2.1 泛型类

泛型类的声明

1
2
3
4
5
6
7
8
class KvPair<K, V> {
K key;
V value;
}


KvPair<Integer, String> pair1 = new KvPair<>(); // 正确
KvPiar<int, string> pair2 = new KvPair<>(); // 错误,不能是基本类型

泛型的类型只能是类,不能是基本类型

2.2 泛型接口

泛型接口的声明

1
2
3
4
5
6
7
8
9
10
11
// 声明一个果篮
interface FruitBasket<T> {
void put(T obj); // 放入水果
T get(); // 取出水果
}

// 苹果篮
class AppleBasket implements FruitBasket<Apple> {}

// 橙子篮
class OrangeBasket implements FruitBasket<Orange> {}

2.3 泛型方法

泛型类中使用泛型方法

1
2
3
4
5
6
7
8
9
10
// 声明一个盒子,泛型定义在类上,方法使用类的泛型声明
class Box<T> {

void put(T data) {}

T get() {
return null;
}

}

非泛型类中使用泛型方法

1
2
3
4
5
6
7
8
9
10
11
// 声明一个盒子,泛型定义在类上,方法使用类的泛型声明
// put方法、get方法不是同一个泛型类型,虽然泛型标识都是T
class Box {

<T> void put(T data) {}

<T> T get() {
return null;
}

}

静态方法上使用泛型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public static class Box<T> {

void put(T data) {}

T get() {
return null;
}

// 因为类已定义了泛型T,在静态方法中不能再使用T
public static <E> Box<E> of() {
return new Box<>();
}

}

2.4 泛型的上界、下界

在使用泛型的时候,我们可以为传入的泛型类型实参进行上下边界的限制。

泛型上界:传入的类型实参必须是指定类型或者指定类型的子类型

1
2
3
4
List<? extends Number> list = new ArrayList<>();
list.add(new Integer(1)); // 错误, Integer是Number的子类型
list.add(new Double(2.0)); // 错误, Double是Number的子类型
Number obj = list.get(0);

List<? extends T>是被设计用来读取数据的泛型,并且只能读取类型为T的元素。如果要满足List<? extends T>能添加元素,需满足List中的任何一个元素都能被转换为T的子类,所以List<? extends T>不能用来写入元素。

泛型下界:传入的类型实参必须是指定类型或者指定类型的父类型

1
2
3
4
List<? super Number> list = new ArrayList<>();
list.add(new Integer(1)); // 正确, Integer是Number的子类型
list.add(new Double(2.0)); // 正确, Double是Number的子类型
Object obj = list.get(0); // 正确,但是需要强转为指定类型,失去了泛型的意义

List<? super T>是被设计用来存储数据的,只能添加T类型或其子类类型。因为List中的任何一个元素都能向上转型为T类型或者T的父类型。

PECS
PECS是”Producer Extends,Consumer Super”(生产者用Extends,消费者用Super)的缩写。

  • “Producer Extends”的意思是,如果需要一个List去生产T类型的数据(需要从List中读取T类型实例),就声明List中的元素为<? extends T>,例如List<? extends Integer>,但是不能往里面添加元素。
  • “Consumer Super”的意思是,如果需要一个List去消费T类型的数据(需要往List中添加T类型实例),就声明List中的元素为<? super T>,例如List<? super Integer>。但是不能保证从这个List中读取出来的数据类型。

2.5 无界通配符与向上转型

向上转型是指用子类实例去初始化父类,这是面向对象中多态的重要表现。

语法形式:<?>,不依赖于类型参数的泛型,不能添加数据,示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
List<?> list = new ArrayList<>();
list.add(new Integer(1)); // 错误
list.add(new Double(2.0)); // 错误
list.add(new Object()); // 错误
Object obj = list.get(0); // 失去了泛型的意义

void print1(List<Number> list);

void print2(List<?> list);

public static void main() {
print1(new ArrayList<Integer>()); // 错误
print2(new ArrayList<Integer>()); // 正确
}

泛型不能向上转型,但是可以通过使用通配符来向上转型

1
2
List<? extends Integer> list1 = new ArrayList<>();
List<? extends Number> list2 = list1; // 正确

2.6 泛型数组

1
2
3
4
5
6
List<String>[] list1 = new ArrayList<String>[10]; //编译错误 
List<String>[] list2 = new ArrayList<?>[10]; //编译错误
List<String>[] list3 = (List<String>[]) new ArrayList<?>[10]; //正确,有警告
List<?>[] list4 = new ArrayList<String>[10]; //编译错误
List<?>[] list5 = new ArrayList<?>[10]; // 正确
List<String>[] list6 = new ArrayList[10]; //正确,有警告

3. 深入理解Java泛型

3.1 泛型擦除

Java的泛型实现采取了“伪泛型”的策略,即语法上支持泛型,但是在编译阶段会进行“类型擦除”,将所有的泛型表示(尖括号中的内容)都替换为具体的类型。

类型擦除的方式:

  • 删除类型参数声明,即删除<>的部分

  • 根据上下界推断,将泛型类型替换为具体类型:如果是无界通配符(?)或者无上下界限制则替换为Object;存在上下界限制时则替换为限制的类型

  • 插入强转换代码

    1
    2
    3
    class Box<T> { T data; }  ==>  class Box { Object data; } 
    class Box<T extends Number> { T data; } ==> class Box { Number data; }
    class Box<T super Number> { T data; } ==> class Box { Number data; }

3.2 泛型和反射

  • 泛型的实例化
    因为泛型不存在Class文件,所以泛型不能直接实例化;但是可以通过反射进行实例化

    1
    2
    3
    T obj = new T(); // 错误
    Class<T> clazz;
    T obj = (T) clazz.newInstance(); // 正确

Java泛型-概念
https://blog.jervain.site/2025/03f8f6a73c.html
作者
Jervain
发布于
2025年3月25日
许可协议