【从零开始java学习|第二十三篇】泛型体系与通配符
泛型是 Java 集合框架的核心特性之一,它通过参数化类型实现代码复用与类型安全。以下从泛型类、泛型方法、泛型接口、泛型通配符四个维度展开,并结合集合的继承关系说明其应用。
一、泛型类:集合的 “容器模板”
定义与语法
泛型类是在类定义时引入类型参数(如 <T>
),使类的成员(变量、方法)可操作任意类型。集合框架中,ArrayList<E>
、HashMap<K,V>
等均为泛型类。
// 自定义泛型类:MyList<T>,模拟ArrayList的简化版
class MyList<T> {private Object[] elements;private int size;public MyList() {elements = new Object[10];}// 泛型方法:添加元素(T类型)public void add(T item) {elements[size++] = item;}// 泛型方法:获取元素(返回T类型)public T get(int index) {return (T) elements[index];}
}
集合中的应用
ArrayList<E>
:通过泛型类实现 “动态数组”,元素类型由<E>
指定,避免装箱拆箱开销。HashMap<K,V>
:键值对的泛型类,<K>
为键类型,<V>
为值类型,保证键值对的类型安全。
二、泛型方法:集合工具的 “通用逻辑”
定义与语法
泛型方法是在方法返回值前声明类型参数(如 <T>
),使其独立于类的泛型参数。集合工具类Collections
中的sort
、binarySearch
等均为泛型方法。
// 自定义泛型方法:打印任意类型数组
public class CollectionUtils {public static <T> void printArray(T[] array) {for (T item : array) {System.out.print(item + " ");}System.out.println();}
}
泛型不具备继承性
泛型类型之间不存在继承关系,即使两个类存在继承关系,使用它们作为泛型参数的泛型类型之间也不会自动具有继承关系。例如,假设有类Animal
和它的子类Dog
,ArrayList<Dog>
不是 ArrayList<Animal>
的子类。
用代码示例可以更直观地说明:
import java.util.ArrayList;class Animal {}
class Dog extends Animal {}public class GenericInheritanceTest {public static void main(String[] args) {// 编译错误,因为ArrayList<Dog>不是ArrayList<Animal>的子类// ArrayList<Animal> animalList = new ArrayList<Dog>(); }
}
之所以会出现编译错误,是因为泛型类型的设计目的是提供类型安全。如果允许ArrayList<Dog>
是 ArrayList<Animal>
的子类,那么当从ArrayList<Animal>
中获取元素时,编译器无法保证获取到的一定是Animal
,可能会导致运行时的类型转换异常。比如:
import java.util.ArrayList;class Animal {}
class Dog extends Animal {}
class Cat extends Animal {}public class GenericInheritanceError {public static void main(String[] args) {ArrayList<Dog> dogList = new ArrayList<>();dogList.add(new Dog());// 假设允许这样的赋值ArrayList<Animal> animalList = dogList; animalList.add(new Cat()); // 这里编译时不会报错Dog dog = dogList.get(1); // 运行时会抛出ClassCastException,因为索引1处是Cat对象}
}
数据具备继承性
虽然泛型类型本身不具备继承性,但存储在泛型集合中的数据是具备继承性的。当定义一个泛型集合,比如ArrayList<Animal>
时,由于Dog
、Cat
等类继承自Animal
,所以可以将Dog
、Cat
等Animal
子类的对象添加到ArrayList<Animal>
中。
示例代码如下:
import java.util.ArrayList;class Animal {}
class Dog extends Animal {}
class Cat extends Animal {}public class DataInheritanceTest {public static void main(String[] args) {ArrayList<Animal> animalList = new ArrayList<>();animalList.add(new Dog()); // 可以添加Animal的子类对象animalList.add(new Cat());for (Animal animal : animalList) {// 可以按照Animal类型进行处理,也可以根据实际类型进行类型转换后处理System.out.println(animal.getClass().getSimpleName()); }}
}
这是因为 Java 的继承机制允许子类对象替代父类对象出现在需要父类对象的地方,也就是常说的里氏替换原则。在泛型集合中,只要集合声明的泛型类型是数据类型的父类,那么就可以将数据类型(子类)的对象添加进去,并且在遍历处理这些对象时,可以按照父类的方式统一处理,也可以根据实际的子类类型进行针对性处理。
集合中的应用
Collections.sort(List<T>)
:通过泛型方法实现对任意List
的排序,类型由<T>
自动推断。Stream<T> map(Function<T, R>)
:Stream API 的泛型方法,支持链式操作中的类型转换。
三、泛型接口:集合的 “行为契约”
定义与语法
泛型接口是在接口定义时引入类型参数(如 <E>
),实现类需指定具体类型或继续使用泛型。集合框架中,List<E>
、Set<E>
、Map<K,V>
均为泛型接口。
// 自定义泛型接口:表示“可迭代”的集合
interface Iterable<T> {Iterator<T> iterator();
}// 实现类:ArrayList实现Iterable<E>
class ArrayList<E> implements Iterable<E> {@Overridepublic Iterator<E> iterator() {return new ArrayListIterator();}private class ArrayListIterator implements Iterator<E> {// 迭代逻辑...}
}
集合中的应用
List<E>
:定义 “有序集合” 的契约,子类(如ArrayList
、LinkedList
)需实现其方法。Comparable<T>
:泛型接口,用于对象排序(如Integer
实现Comparable<Integer>
)。
四、泛型通配符:处理集合的 “继承关系”
泛型本身不支持协变(子类型自动转换),需通过通配符(?
、? extends T
、? super T
)解决集合的继承兼容问题。
1. 无界通配符 ?
表示 “任意类型”,用于集合仅需操作Object
方法(如toString()
、equals()
)的场景。
// 打印任意类型的集合
public static void printCollection(Collection<?> coll) {for (Object item : coll) {System.out.println(item);}
}
集合中的应用:
- 通用工具方法(如打印任意
List
、Set
)。
2. 上限通配符 ? extends T
表示 “T
或T
的子类”,遵循PECS 原则(Producer Extends)—— 仅用于 “读取” 数据(生产者)。
// 计算数值集合的总和(T必须是Number的子类)
public static double sum(Collection<? extends Number> nums) {double total = 0;for (Number num : nums) {total += num.doubleValue();}return total;
}
集合中的应用:
List<? extends Number>
可接收List<Integer>
、List<Double>
等,实现数值类型的统一处理。
3. 下限通配符 ? super T
表示 “T
或T
的父类”,遵循PECS 原则(Consumer Super)—— 仅用于 “写入” 数据(消费者)。
// 向集合中添加整数(集合类型必须是Integer或其父类)
public static void addIntegers(List<? super Integer> list) {list.add(10);list.add(20);
}
集合中的应用:
List<? super Integer>
可接收List<Integer>
、List<Number>
、List<Object>
,实现数据的向上兼容存储。
4. 泛型继承关系的处理
泛型不支持直接协变(如List<Integer>
不是List<Number>
的子类),但通过通配符可间接建立关系:
// 错误:泛型不支持直接协变
// List<Number> nums = new ArrayList<Integer>(); // 正确:通过上限通配符建立关系
List<? extends Integer> intList = new ArrayList<Integer>();
List<? extends Number> numList = intList; // 合法,因为Integer是Number的子类
五、总结:泛型在集合中的核心价值
- 类型安全:编译期检查集合元素类型,避免
ClassCastException
。 - 代码复用:泛型类 / 方法 / 接口实现 “一次编写,多类型适用”(如
ArrayList
可存储任意类型)。 - 灵活兼容:通配符(
? extends T
、? super T
)解决泛型与继承的兼容问题,适配复杂场景。
如果我的内容对你有帮助,请点赞,评论,收藏。接下来我将继续更新相关内容!