Skip to content

🔷 泛型

泛型概述

泛型(Generics)是 Java 5 引入的特性,允许在定义类、接口和方法时使用类型参数,提高代码的类型安全性和可重用性。

泛型的好处

  • 类型安全:编译时检查类型
  • 代码复用:一个类可以处理多种类型
  • 消除类型转换:自动处理类型转换
  • 提高可读性:代码更清晰

泛型类

基本语法

java
// 泛型类定义
public class Box<T> {
    private T value;
    
    public void setValue(T value) {
        this.value = value;
    }
    
    public T getValue() {
        return value;
    }
}

// 使用泛型类
Box<String> stringBox = new Box<>();
stringBox.setValue("Hello");
String str = stringBox.getValue();

Box<Integer> intBox = new Box<>();
intBox.setValue(10);
Integer num = intBox.getValue();

多个类型参数

java
// 多个类型参数
public class Pair<K, V> {
    private K key;
    private V value;
    
    public Pair(K key, V value) {
        this.key = key;
        this.value = value;
    }
    
    public K getKey() {
        return key;
    }
    
    public V getValue() {
        return value;
    }
}

// 使用
Pair<String, Integer> pair = new Pair<>("age", 25);
String key = pair.getKey();
Integer value = pair.getValue();

泛型接口

基本语法

java
// 泛型接口
public interface List<T> {
    void add(T item);
    T get(int index);
    int size();
}

// 实现泛型接口
public class ArrayList<T> implements List<T> {
    private T[] items;
    private int size;
    
    @Override
    public void add(T item) {
        // 实现
    }
    
    @Override
    public T get(int index) {
        return items[index];
    }
    
    @Override
    public int size() {
        return size;
    }
}

泛型方法

基本语法

java
// 泛型方法
public class Util {
    public static <T> T getFirst(T[] array) {
        return array[0];
    }
    
    public static <T> void swap(T[] array, int i, int j) {
        T temp = array[i];
        array[i] = array[j];
        array[j] = temp;
    }
}

// 使用
String[] strArray = {"a", "b", "c"};
String first = Util.getFirst(strArray);

Integer[] intArray = {1, 2, 3};
Integer firstInt = Util.getFirst(intArray);

类型通配符

上界通配符(? extends)

java
// 上界通配符:只能读取,不能写入
public void process(List<? extends Number> list) {
    // 只能读取
    Number num = list.get(0);
    // ❌ 不能写入
    // list.add(10);  // 编译错误
}

// 使用
List<Integer> intList = Arrays.asList(1, 2, 3);
process(intList);  // ✅ 可以

List<Double> doubleList = Arrays.asList(1.0, 2.0, 3.0);
process(doubleList);  // ✅ 可以

下界通配符(? super)

java
// 下界通配符:只能写入,不能读取
public void addNumber(List<? super Integer> list) {
    list.add(10);  // ✅ 可以写入
    // ❌ 不能读取
    // Integer num = list.get(0);  // 编译错误
}

// 使用
List<Number> numberList = new ArrayList<>();
addNumber(numberList);  // ✅ 可以

List<Object> objectList = new ArrayList<>();
addNumber(objectList);  // ✅ 可以

无界通配符(?)

java
// 无界通配符:只能读取,不能写入
public void printList(List<?> list) {
    for (Object obj : list) {
        System.out.println(obj);
    }
    // ❌ 不能写入
    // list.add("item");  // 编译错误
}

// 使用
List<String> strList = Arrays.asList("a", "b", "c");
printList(strList);  // ✅ 可以

List<Integer> intList = Arrays.asList(1, 2, 3);
printList(intList);  // ✅ 可以

类型擦除

类型擦除原理

java
// 编译前
public class Box<T> {
    private T value;
    
    public void setValue(T value) {
        this.value = value;
    }
    
    public T getValue() {
        return value;
    }
}

// 编译后(类型擦除)
public class Box {
    private Object value;
    
    public void setValue(Object value) {
        this.value = value;
    }
    
    public Object getValue() {
        return value;
    }
}

类型擦除的影响

java
// ❌ 不能创建泛型数组
// T[] array = new T[10];  // 编译错误

// ✅ 使用 Object 数组
Object[] array = new Object[10];

// ❌ 不能使用 instanceof
// if (obj instanceof Box<String>) { }  // 编译错误

// ✅ 使用原始类型
if (obj instanceof Box) { }  // 可以

泛型限制

不能使用基本类型

java
// ❌ 错误:不能使用基本类型
// Box<int> box = new Box<>();  // 编译错误

// ✅ 正确:使用包装类
Box<Integer> box = new Box<>();

不能创建泛型数组

java
// ❌ 错误:不能创建泛型数组
// List<String>[] lists = new List<String>[10];  // 编译错误

// ✅ 正确:使用通配符
List<?>[] lists = new List<?>[10];

不能实例化类型参数

java
// ❌ 错误:不能实例化类型参数
// public class Box<T> {
//     private T value = new T();  // 编译错误
// }

// ✅ 正确:使用反射或工厂方法
public class Box<T> {
    private T value;
    
    public Box(Class<T> clazz) throws Exception {
        this.value = clazz.newInstance();
    }
}

完整示例

java
// 泛型类
public class GenericBox<T> {
    private T value;
    
    public GenericBox(T value) {
        this.value = value;
    }
    
    public void setValue(T value) {
        this.value = value;
    }
    
    public T getValue() {
        return value;
    }
}

// 泛型方法
public class GenericUtil {
    public static <T> T getFirst(List<T> list) {
        return list.isEmpty() ? null : list.get(0);
    }
    
    public static <T extends Comparable<T>> T max(T a, T b) {
        return a.compareTo(b) > 0 ? a : b;
    }
}

// 使用
public class GenericExample {
    public static void main(String[] args) {
        // 泛型类
        GenericBox<String> stringBox = new GenericBox<>("Hello");
        String str = stringBox.getValue();
        
        GenericBox<Integer> intBox = new GenericBox<>(10);
        Integer num = intBox.getValue();
        
        // 泛型方法
        List<String> strList = Arrays.asList("a", "b", "c");
        String first = GenericUtil.getFirst(strList);
        
        Integer max = GenericUtil.max(5, 10);
    }
}

最佳实践

1. 使用有意义的类型参数名

java
// ✅ 好的做法:使用有意义的名称
public class Box<T> { }
public class Map<K, V> { }
public class Pair<First, Second> { }

// ❌ 不好的做法:使用单个字母(除非是惯用)
public class Box<T1> { }

2. 优先使用通配符

java
// ✅ 好的做法:使用通配符提高灵活性
public void process(List<? extends Number> list) { }

// ❌ 不好的做法:使用具体类型限制灵活性
public void process(List<Integer> list) { }

3. 避免原始类型

java
// ❌ 不好的做法:使用原始类型
List list = new ArrayList();

// ✅ 好的做法:使用泛型
List<String> list = new ArrayList<>();

下一步

掌握了泛型后,可以继续学习:


💡 提示:泛型提供了类型安全和代码复用,合理使用泛型可以编写更优雅的代码