一、为啥要有泛型?
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> 限制下限。 |
最佳实践 | 优先使用泛型类/方法提升类型安全,复杂场景结合通配符精准控制类型范围。 |