Java 泛型

文章目录

Java 反射中,Class 类和 Constructor 类后都有 <?> 的标识,这个标识表示“任意类型”,即泛型。通过这个标识,一个 Class 类可以存储任何类型的 Class 对象。

泛型的定义与使用

泛型标记符

上面提到的 <?> 中的 ? 是一种常用的标记符。Java 中有如下常用标记符:

标记符 含义
E Element - 在集合中使用,表示集合元素
T Type - 类型
K Key - 键
V Value - 值,常与 K 搭配使用
N Number - 数值
? 无法确定类型

提示

上面提到的标记符除 ? 外均为命名惯例,即使把代码中所有的 T 都改成 D 甚至 GDN,代码也是符合命名规范的。

泛型类

泛型类是指具有一个或多个类型参数的类。

 1package com.jackgdn.entities;
 2
 3import java.util.Optional;
 4
 5public class Box<T> { // 泛型类 Box,T 是类型参数
 6    private T content; // Box 类中 content 属性的类型需要在实例化时指定
 7
 8    public Box() {
 9        this.content = null;
10    }
11
12    public Box(T content) {
13        this.content = content;
14    }
15
16    public void setContent(T content) {
17        this.content = content;
18    }
19
20    public T getContent() { // 该方法的返回值类型是 T,即 Box 中存储的内容的类型
21        return content;
22    }
23
24    public String getTypeName() {
25        return Optional.ofNullable(content)
26                .map(c -> c.getClass())
27                .map(c -> c.getName())
28                .orElse(null);
29    }
30}
 1package com.jackgdn;
 2
 3import com.jackgdn.entities.Box;
 4
 5public class Main {
 6    public static void main(String[] args) {
 7        Box<String> box = new Box<>("Hello, World!");
 8        System.out.println("Content: " + box.getContent());
 9        System.out.println("Type Name: " + box.getTypeName());
10    }
11}
12
13/*
14 * output:
15 * Content: Hello, World!
16 * Type Name: java.lang.String
17 */

泛型方法

泛型方法是指在方法声明中定义了类型参数的方法。

 1package com.jackgdn.entities;
 2
 3public class Sorter {
 4    public static <T extends Comparable<T>> void sort(T[] array) { // 上限通配符,表示 T 必须实现 Comparable 接口
 5        if (array == null || array.length < 2) {
 6            return;
 7        }
 8
 9        for (int i = 0; i < array.length - 1; i++) { // 使用冒泡排序算法
10            for (int j = 0; j < array.length - 1 - i; j++) {
11                if (array[j].compareTo(array[j + 1]) > 0) {
12                    T temp = array[j];
13                    array[j] = array[j + 1];
14                    array[j + 1] = temp;
15                }
16            }
17        }
18    }
19}
 1package com.jackgdn;
 2
 3import java.util.Arrays;
 4
 5import com.jackgdn.entities.Sorter;
 6
 7public class Main {
 8    public static void main(String[] args) {
 9        Integer[] integers = { 5, 3, 5, 8, 9, 7, 9, 3, 2, 3 };
10        Sorter.sort(integers);
11        System.out.println(Arrays.toString(integers));
12
13        String[] strings = { "Hello", "World", "Java", "Generics" };
14        Sorter.sort(strings);
15        System.out.println(Arrays.toString(strings));
16    }
17}
18
19/*
20 * output:
21 * [2, 3, 3, 3, 5, 5, 7, 8, 9, 9]
22 * [Generics, Hello, Java, World]
23 */

Sorter.sort 方法中 <T extends Comparable<T>> 是一个上限通配符,一般写法是 <? extends T>,表示 ? 类型是 T 类型的子类/实现了 T 接口;与之对应的是下限通配符,一般写法是 <? super T>,表示 ? 类型是 T 的祖先类。

泛型接口

 1package com.jackgdn.interfaces;
 2
 3// 泛型接口
 4public interface Pair<K, V> {
 5
 6    K getKey();
 7
 8    V getValue();
 9
10    void setKey(K key);
11
12    void setValue(V value);
13
14}
 1package com.jackgdn.entities;
 2
 3import com.jackgdn.interfaces.Pair;
 4
 5import lombok.*;
 6
 7@Data
 8@NoArgsConstructor
 9@AllArgsConstructor
10// 实现 Pair 泛型接口
11public class OrderedPair<K, V> implements Pair<K, V> {
12    private K key;
13    private V value;
14}
 1package com.jackgdn;
 2
 3import com.jackgdn.entities.OrderedPair;
 4import com.jackgdn.interfaces.Pair;
 5
 6public class Main {
 7    public static void main(String[] args) {
 8        // 创建键为 String,值为 Double 的 Pair
 9        Pair<String, Double> pair1 = new OrderedPair<>("PI", 3.14);
10
11        // 创建键为 Integer,值为 Boolean 的 Pair
12        Pair<Integer, Boolean> pair2 = new OrderedPair<>(null, null);
13        pair2.setKey(1);
14        pair2.setValue(true);
15
16        System.out.println(pair1);
17        System.out.println(pair2);
18    }
19}
20
21/*
22 * output:
23 * OrderedPair(key=PI, value=3.14)
24 * OrderedPair(key=1, value=true)
25 */

类型擦除

Java 泛型是通过类型擦除实现的。在编译时,所有泛型类型信息都会被擦除:

  1. 如果泛型类型没有边界(如 <T>),则所有的 T 都会被替换为 Object
  2. 如果泛型类型有边界(如 <T extends Comparable<T>>),则所有的 T 都会被替换为 Comparable

OrderedPair 中的例子来说,当通过 Pair<String, Double> pair1 = new OrderedPair<>("PI", 3.14); 创建 pair1 之后,pair1 中的所有 K 都会变为 String 类型,所有 V 都会变为 Double 类型。

 1package com.jackgdn;
 2
 3import java.lang.reflect.Constructor;
 4import java.util.Arrays;
 5
 6import com.jackgdn.entities.Box;
 7
 8public class Main {
 9    public static void main(String[] args) {
10        Box<String> stringBox = new Box<>("GDN");
11        Class<?> boxClass = stringBox.getClass(); // 获取 stringBox 的运行时类型
12
13        try {
14            // stringBox 中已经发生类型擦除,其含参构造方法的参数被指定为了 Object 类型
15            Constructor<?> boxConstructor = boxClass.getConstructor(Object.class);
16            System.out.println(Arrays.toString(boxConstructor.getParameterTypes()));
17        } catch (NoSuchMethodException e) {
18            e.printStackTrace();
19        }
20    }
21}
22
23/*
24 * output:
25 * [class java.lang.Object]
26 */

重要

泛型的限制

  1. 不能实例化泛型类型:不能使用 new T() 这样的语法
  2. 不能创建泛型数组:不能声明 T[] array = new T[10]
  3. 静态成员不能使用类的泛型类型参数:静态方法和字段属于类,而不是实例
  4. 不能使用基本数据类型作为泛型类型参数:必须使用包装类(如 Integer 而不是 int
  5. 不能捕获泛型类型的异常:catch 块不能使用泛型类型参数