说一下泛型原理,并举例说明

泛型原理

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 为某子类的方法而不是擦除后的类的方法)

posted @ 2025-08-20 20:40  一刹流云散  阅读(27)  评论(0)    收藏  举报