说一下泛型原理,并举例说明
泛型原理
1. 泛型的基本概念
泛型(Generics)是Java 5引入的特性,允许在定义类、接口和方法时使用类型参数,使得同一份代码可以处理不同类型的对象,同时保持类型安全。
2. 泛型的核心原理
类型参数化
- 使用类型参数(如
<T>、<E>、<K,V>)代替具体类型 - 在使用时指定具体类型,编译器进行类型检查和替换
类型擦除(Type Erasure)
这是Java泛型实现的核心机制:
- 编译时擦除类型参数,替换为Object或指定的边界类型
- 在需要的地方插入类型转换代码
- 保持与旧版本Java的兼容性
// 编译前的泛型代码
public class Box<T> {
private T item;
public void setItem(T item) {
this.item = item;
}
public T getItem() {
return item;
}
}
// 编译后的字节码(概念上)
public class Box {
private Object item;
public void setItem(Object item) {
this.item = item;
}
public Object getItem() {
return item;
}
}
3. 泛型的使用示例
泛型类示例
// 泛型类定义
public class Container<T> {
private T value;
public Container(T value) {
this.value = value;
}
public T getValue() {
return value;
}
public void setValue(T value) {
this.value = value;
}
}
// 使用示例
Container<String> stringContainer = new Container<>("Hello");
Container<Integer> intContainer = new Container<>(123);
String str = stringContainer.getValue(); // 无需强制转换
Integer num = intContainer.getValue(); // 无需强制转换
泛型接口示例
// 泛型接口定义
public interface GenericInterface<T> {
void add(T item);
T get(int index);
int size();
}
// 实现泛型接口
public class StringList implements GenericInterface<String> {
private List<String> list = new ArrayList<>();
@Override
public void add(String item) {
list.add(item);
}
@Override
public String get(int index) {
return list.get(index);
}
@Override
public int size() {
return list.size();
}
}
泛型方法示例
public class Utility {
// 泛型方法
public static <T> void swap(T[] array, int i, int j) {
T temp = array[i];
array[i] = array[j];
array[j] = temp;
}
// 带边界限制的泛型方法
public static <T extends Comparable<T>> T findMax(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) {
max = array[i];
}
}
return max;
}
}
// 使用示例
String[] names = {"Alice", "Bob", "Charlie"};
Utility.swap(names, 0, 2);
Integer[] numbers = {1, 5, 3, 9, 2};
Integer maxNumber = Utility.findMax(numbers); // 返回9
4. 泛型边界
上界通配符(Upper Bounded Wildcards)
// 限定类型参数必须是Number或其子类
public static double sumOfList(List<? extends Number> list) {
double sum = 0.0;
for (Number num : list) {
sum += num.doubleValue();
}
return sum;
}
List<Integer> intList = Arrays.asList(1, 2, 3);
List<Double> doubleList = Arrays.asList(1.1, 2.2, 3.3);
double sum1 = sumOfList(intList); // 正确
double sum2 = sumOfList(doubleList); // 正确
下界通配符(Lower Bounded Wildcards)
// 限定类型参数必须是Integer或其父类
public static void addNumbers(List<? super Integer> list) {
for (int i = 1; i <= 10; i++) {
list.add(i);
}
}
List<Integer> intList = new ArrayList<>();
List<Number> numberList = new ArrayList<>();
List<Object> objectList = new ArrayList<>();
addNumbers(intList); // 正确
addNumbers(numberList); // 正确
addNumbers(objectList); // 正确
无界通配符
// 可以接受任何类型的List
public static void printList(List<?> list) {
for (Object elem : list) {
System.out.print(elem + " ");
}
System.out.println();
}
5. 泛型的限制
不能使用基本类型
// 错误:不能使用基本类型
// List<int> intList = new ArrayList<int>();
// 正确:使用包装类型
List<Integer> intList = new ArrayList<Integer>();
不能创建类型参数的实例
public class GenericClass<T> {
// 错误:不能创建T的实例
// private T instance = new T();
// 正确:通过工厂方法或传递实例
private T instance;
public GenericClass(T instance) {
this.instance = instance;
}
}
不能声明类型参数的静态成员
public class GenericClass<T> {
// 错误:不能声明静态的T类型成员
// private static T staticField;
// 正确:可以声明静态方法使用泛型
public static <T> T getDefaultValue() {
return null;
}
}
6. 泛型的实际应用场景
集合框架
List<String> stringList = new ArrayList<>();
Map<String, Integer> map = new HashMap<>();
Set<Double> set = new HashSet<>();
自定义泛型数据结构
public class GenericStack<T> {
private List<T> elements = new ArrayList<>();
public void push(T item) {
elements.add(item);
}
public T pop() {
if (elements.isEmpty()) {
throw new EmptyStackException();
}
return elements.remove(elements.size() - 1);
}
public boolean isEmpty() {
return elements.isEmpty();
}
}
泛型就是将类型变成参数传入,使得可以使用的类型多样化,从而实现解耦。Java 泛型是在
Java1.5 以后出现的,为保持对以前版本的兼容,使用了擦除的方法实现泛型。擦除是指在
一定程度无视类型参数 T,直接从 T 所在的类开始向上 T 的父类去擦除,如调用泛型方法,
传入类型参数 T 进入方法内部,若没在声明时做类似 public T methodName(T extends Father
t){},Java 就进行了向上类型的擦除,直接把参数 t 当做 Object 类来处理,而不是传进去的 T。
即在有泛型的任何类和方法内部,它都无法知道自己的泛型参数,擦除和转型都是在边界上
发生,即传进去的参在进入类或方法时被擦除掉,但传出来的时候又被转成了我们设置的 T。
在泛型类或方法内,任何涉及到具体类型(即擦除后的类型的子类)操作都不能进行,如
new T(),或者 T.play()(play 为某子类的方法而不是擦除后的类的方法)

浙公网安备 33010602011771号