Java 泛型

泛型(Generics)让"类型"也能被参数化:写类或方法时先用占位符,使用时再指定具体类型。它把类型错误提前到编译期发现,省去大量强转,集合框架是最大的受益者。

泛型解决什么问题

没有泛型时,集合里装的是 Object,取出必须强转,转错了要到运行期才暴露:

// 不用泛型:取出要强转,容易运行期出错
List list = new ArrayList();
list.add("hello");
String s1 = (String) list.get(0);       // 每次都强转

// 用泛型:编译期就确定元素类型
List<String> list2 = new ArrayList<>();
list2.add("hello");
String s2 = list2.get(0);               // 免强转,类型安全

泛型类 Box<T>

类名后用尖括号声明类型参数,类内部就能用 T 充当任意类型:

class Box<T> {                          // T 是类型参数
    private T item;
    public void set(T item) { this.item = item; }
    public T get() { return item; }
}

// —— 片段需放进 main 方法中运行 ——
Box<String> box = new Box<>();
box.set("苹果");
System.out.println(box.get());
// 输出:苹果

类型参数命名约定:E 元素、K 键、V 值、T 类型、N 数字,见名知意。

泛型方法

方法声明中在返回值前加 <T>,该 T 只对本方法有效,参数与返回值可共用:

public class Demo {
    public static <T> void printArr(T[] arr) {   // 泛型方法
        for (T t : arr) System.out.print(t + " ");
        System.out.println();
    }

    public static void main(String[] args) {     // 完整程序
        printArr(new Integer[]{1, 2, 3});
        printArr(new String[]{"A", "B"});
    }
}
// 输出:1 2 3 
// 输出:A B 

通配符 ? 、? extends 与 ? super

通配符用于"不确定具体类型"的场合:

  • List<?>:任何类型的 List 都能接收,但只能读、不能往里写。
  • List<? extends Number>:上界通配符,可接收 Number 及其子类(Integer、Double…),适合读。
  • List<? super Integer>:下界通配符,可接收 Integer 及其父类(Number、Object…),适合写。
public static void show(List<? extends Number> list) {
    for (Number n : list) System.out.println(n);   // 读出的是 Number
}

泛型集合的典型用法如 List<String>Map<String, Integer>Set<Double>,声明后就带上类型约束。

类型擦除与不可协变

  • 类型擦除:编译后泛型信息会被抹掉,List<String> 变成原始类型 List,运行期拿不到 T 的真实类型。
  • 不可协变:List<String> 不是 List<Object> 的子类型,直接传参会编译报错,这是为了类型安全;需要"多类型兼容"时用 ? extends 通配符。

泛型把类型检查从运行期提前到编译期:泛型类/方法定义模板,通配符负责灵活兼容,类型擦除提醒我们运行期没有 T——用好它,代码既安全又少写强转。

笔记加载中…