Skip to content

什么是泛型?

泛型(Generics)是 Java 5 引入的特性,允许在定义类、接口或方法时使用类型参数。通过泛型,可以在编译时检查类型安全,避免强制类型转换,并提高代码重用性。

泛型类

使用场景:当一个类中,某个变量的数据类型不确定时,就可以定义带有泛型的类。
语法格式
在类名后添加类型参数(如< T >),表示该类可以处理任意类型。可定义多个参数,用尖括号 <> 包裹,多个参数用逗号分隔(如 <T, U>)。

java
//语法格式:
修饰符 class 类名<类型参数列表> {
    
 }

 //例:此处T可以理解为变量,但不是用来记录数据的,而是记录数据的类型,可以写成E,K,V等。
 public class Box<T> {
    private T content;
    public void setContent(T content) { this.content = content; }
    public T getContent() { return content; }
}

尖括号<>中的泛型标识被称作类型参数,用于指代任何数据类型。泛型标识可以任意设置,常见泛型标识和含义如下:

  • T:代表一般的任何类;
  • E:代表element元素的意思,或者exception异常的意思;
  • K:代表key的意思;
  • V:代表value的意思,通常与K一起使用;

泛型接口

使用场景:当一个接口中参数类型不确定的时候,可以使用泛型接口。
语法格式
在接口名后添加类型参数(如< T >),可定义多个参数(如<K, V>)。

java
//语法格式:
修饰符 interface 接口名<类型参数列表> {
     /* 接口体 */ 
}
//例:
public interface Pair<K, V> {
    K getKey();
    V getValue();
}

如何使用一个带泛型的接口:

  • 1.实现类给出具体类型。
  • 2.实现类延续泛型,创建对象时再指定泛型类型。

泛型方法

使用场景:当一个方法的形参不确定的情况下,会使用到泛型方法。
语法格式

java
//语法格式:
修饰符 <类型参数列表> 返回类型 方法名(参数列表) {
     /* 方法体 */
}
//例:此处T可以理解为变量,但是不是用来记录数据的,是用来记录类型的,还可以用K,V等。
public class Utils {
    public static <T> T getMiddle(T[] array) {
        return array[array.length / 2];
    }
}

方法中形参类型不确定时候,可以有以下两种方案:

  • 1.使用类名后面定义的泛型:所有方法都能用
  • 2.在方法申明上定义自己的泛型:只有本方法能用

类型通配符

  • 1.无界通配符<?> :使用问号?表示未知类型,接受任何类型,但只能读取为Object。示例如下:
java
public void printList(List<?> list) {
    for (Object elem : list) {
        System.out.print(elem + " ");
    }
}
  • 2.上界通配符<? extends T> :接受 T 及其子类,只能安全读取。示例如下:
java
public double sum(List<? extends Number> list) {
    double sum = 0.0;
    for (Number num : list) {
        sum += num.doubleValue();
    }
    return sum;
}
//简写:
public static double sum(List<? extends Number> list) {
    return list.stream().mapToDouble(Number::doubleValue).sum();
}
  • 3.下界通配符<? super T> : 接受 T 及其父类,可安全写入。示例如下:
java
public void addNumbers(List<? super Integer> list) {
    for (int i = 1; i <= 5; i++) {
        list.add(i);
    }
}
public static void addNumbers(List<? super Integer> list) {
    list.add(42); // 允许添加Integer或其子类
}

有界类型参数

限制类型参数的范围,如 < T extends Number >

java
public class NumericBox< T extends Number > {
    private T number;

    public NumericBox(T number) {
        this.number = number;
    }

    public double square() {
        return number.doubleValue() * number.doubleValue();
    }
}

//用法:
NumericBox< Integer > intBox = new NumericBox<>(5);
double result = intBox.square(); // 25.0
// NumericBox<String> 会编译错误,因为 String 不是 Number 子类

泛型的限制

  • 1.泛型擦除: 泛型信息在编译后被擦除,转换为原始类型(如 Box< T > 变为 Box)。替换为Object或上界类型。
  • 2.无法使用基本类型:必须使用包装类(如List< Integer >而非List< int >)。
  • 3.不能实例化泛型类型:无法实例化类型参数,如:T obj = new T(); // 编译错误 或new T()或T[] array = new T[10]; 是非法的。
  • 4.无法检查泛型类型:运行时无法判断list instanceof List< String >;无法在运行时获取泛型类型信息,如 if (list instanceof ArrayList< String >) 会编译错误。
  • 5.静态成员不能使用类型参数:public class Box< T > { private static T staticField; // 编译错误 }
  • 6.泛型数组限制:List< String >[] array = new List< String >[10]; // 编译错误 不能直接创建泛型数组,但可通过强制转换实现:T[] array = (T[]) new Object[size]; // 潜在警告,需谨慎使用。
  • 7.不能抛出或捕获泛型异常:try { ... } catch (Exception< T > e) { ... } // 编译错误

Released under the MIT License.