深入理解 Java 泛型:从为啥用到咋高级用

一、为啥要有泛型?

1. 类型安全的痛点

非泛型示例(运行时类型转换风险):

import java.util.ArrayList;  
import java.util.List;  
public class WithoutGeneric {  
    public static void main(String[] args) {  
        List list = new ArrayList();  
        list.add("Hello, World!");  
        list.add(42); // 存入Integer类型  
        list.add(3.14); // 存入Double类型  
        for (Object obj : list) {  
            String str = (String) obj; // 运行时抛出ClassCastException(当obj为非String类型时)  
            System.out.println(str);  
        }  
    }  
}  


问题

  • 手动类型转换易出错,运行时才能发现错误。
  • 集合可存入任意类型,类型安全全靠开发者自觉。

2. 泛型的作用

泛型示例(编译期类型检查):

import java.util.ArrayList;  
import java.util.List;  
public class WithGeneric {  
    public static void main(String[] args) {  
        List<String> list = new ArrayList<>(); // 指定仅存String类型  
        list.add("Hello, World!");  
        // list.add(42); 编译报错:不兼容的类型  
        for (String str : list) {  
            System.out.println(str); // 无需强制转换,类型安全  
        }  
    }  
}  


优势

  • 编译期校验:避免存入不匹配类型,提前暴露错误。
  • 代码简洁:无需手动类型转换,增强可读性。

二、泛型的中高级玩法

1. 泛型类

定义格式

public class Pair<T, U> { // T、U为泛型参数(类型占位符)  
    private T first;  
    private U second;  
    public Pair(T first, U second) {  
        this.first = first;  
        this.second = second;  
    }  
    public T getFirst() { return first; }  
    public U getSecond() { return second; }  
}  


使用示例

public class PairUsage {  
    public static void main(String[] args) {  
        // 实例化时指定具体类型:String和Integer  
        Pair<String, Integer> pair = new Pair<>("Number of apples", 5);  
        String description = pair.getFirst(); // 自动类型匹配  
        Integer count = pair.getSecond();  
        System.out.println(description + ": " + count); // 输出:Number of apples: 5  
    }  
}  


场景

  • 通用数据结构(如容器、键值对)。
  • 避免为不同类型重复编写相似类。

2. 泛型方法

定义格式(带类型参数的方法):

public class GenericMethod {  
    // <T> 声明泛型参数,T必须实现Comparable接口(类型边界)  
    public static <T extends Comparable<T>> T max(T[] array) {  
        if (array == null || array.length == 0) return null;  
        T max = array[0];  
        for (int i = 1; i < array.length; i++) {  
            if (array[i].compareTo(max) > 0) { // 利用Comparable接口比较大小  
                max = array[i];  
            }  
        }  
        return max;  
    }  
}  


调用示例

public class GenericMethodUsage {  
    public static void main(String[] args) {  
        Integer[] intArray = {1, 5, 3, 9, 7};  
        Integer maxInt = GenericMethod.max(intArray); // 自动推断T为Integer  
        System.out.println("Max int: " + maxInt); // 输出:Max int: 9  

        String[] stringArray = {"banana", "apple", "cherry"};  
        String maxString = GenericMethod.max(stringArray); // 自动推断T为String  
        System.out.println("Max string: " + maxString); // 输出:Max string: cherry(按字典序)  
    }  
}  


关键点

  • 泛型方法可独立于类存在,灵活适配不同类型。
  • 通过 <T extends 接口/类> 限定泛型类型边界(如必须实现某个接口)。

3. 通配符(Wildcard)

(1)无界通配符 <?>

作用:表示未知类型,允许任何类型的集合。

import java.util.List;  
public class WildcardExample {  
    public static void printList(List<?> list) { // 接受任意类型的List  
        for (Object obj : list) {  
            System.out.println(obj);  
        }  
    }  
}  


调用示例

List<Integer> intList = new ArrayList<>(Arrays.asList(1, 2, 3));  
List<String> stringList = new ArrayList<>(Arrays.asList("Hello", "World"));  
WildcardExample.printList(intList); // 正常输出  
WildcardExample.printList(stringList); // 正常输出  

(2)上界通配符 <? extends Type>

作用:限定泛型为 Type 的子类或本身(包括直接/间接子类)。

class Animal {}  
class Cat extends Animal {}  
class SiameseCat extends Cat {} // 猫的子类  

public class UpperBoundWildcard {  
    public static void processCats(List<? extends Cat> catList) {  
        for (Cat cat : catList) { // 可安全操作Cat及其子类对象  
            // 例如:cat.eat()  
        }  
    }  
}  


调用示例

List<Cat> catList = new ArrayList<>();  
List<SiameseCat> siameseCatList = new ArrayList<>();  
UpperBoundWildcard.processCats(catList); // 允许  
UpperBoundWildcard.processCats(siameseCatList); // 允许(SiameseCat是Cat的子类)  

(3)下界通配符 <? super Type>

作用:限定泛型为 Type 的父类或本身(包括直接/间接父类)。

public class LowerBoundWildcard {  
    public static void addCat(List<? super Cat> catList) {  
        catList.add(new Cat()); // 安全添加Cat对象(无论父类集合还是自身集合)  
    }  
}  


调用示例

List<Cat> catList = new ArrayList<>();  
List<Animal> animalList = new ArrayList<>(); // Animal是Cat的父类  
LowerBoundWildcard.addCat(catList); // 允许  
LowerBoundWildcard.addCat(animalList); // 允许(Animal是Cat的父类)  

三、总结

特性说明
核心价值编译期类型安全、代码复用、避免强制类型转换。
泛型类通过 <T, U> 定义通用类,实例化时指定具体类型(如 Pair<String, Integer>)。
泛型方法在方法级别定义泛型(如 public static <T> T max(T[] array)),灵活适配不同类型。
通配符? 表示未知类型;<? extends Type> 限制上限;<? super Type> 限制下限。
最佳实践优先使用泛型类/方法提升类型安全,复杂场景结合通配符精准控制类型范围。

发表评论