Java 泛型详解:从入门到实战
🧠 一、什么是泛型?
泛型(Generics)是 Java 5 引入的重要特性之一,它允许在定义类、接口和方法时使用类型参数化。通过泛型,我们可以在编写代码时不指定具体类型,而是在使用时再传入具体的类型。
示例:没有泛型的集合操作
Map map = new HashMap();
map.put("key", "value");
String s = (String) map.get("key"); // 必须强制类型转换
如果有人插入了 Integer
类型的值,运行时会抛出 ClassCastException
。
使用泛型后:
Map<String, String> map = new HashMap<>();
String s = map.get("key"); // 不需要强制类型转换
泛型帮助我们在编译期就发现类型错误,提高了程序的安全性和可读性。
✅ 二、泛型的优势
优势 | 描述 |
---|---|
类型安全 | 编译器会在编译阶段检查类型是否匹配,避免运行时异常 |
消除强制类型转换 | 提高代码可读性和安全性 |
代码复用 | 同一套逻辑适配多种数据类型 |
更好的性能表现 | 减少不必要的类型检查与转换 |
🔨 三、泛型的基本用法
1. 定义泛型类
public class Box<T> {private T item;public void setItem(T item) {this.item = item;}public T getItem() {return item;}
}
使用方式:
Box<String> stringBox = new Box<>();
stringBox.setItem("Hello");
String content = stringBox.getItem(); // 不需要强转
2. 定义泛型接口
public interface Container<T> {void add(T item);T get(int index);
}
实现该接口的类也需要使用泛型:
public class ListContainer<T> implements Container<T> {private List<T> list = new ArrayList<>();public void add(T item) {list.add(item);}public T get(int index) {return list.get(index);}
}
3. 定义泛型方法
public class Util {public static <T> T getFirst(List<T> list) {return list.isEmpty() ? null : list.get(0);}
}
调用方式:
List<String> names = Arrays.asList("Alice", "Bob");
String first = Util.getFirst(names); // 自动推断为 String 类型
📚 四、泛型命名规范
符号 | 含义 |
---|---|
T | Type,通用类型 |
E | Element,集合元素 |
K | Key,键 |
V | Value,值 |
N | Number,数字类型 |
⚠️ 五、泛型的限制与注意事项
1. 泛型不是协变的
List<Integer> intList = new ArrayList<>();
List<Number> numberList = intList; // ❌ 编译错误!
这是为了防止将 Float
添加进 List<Integer>
中,破坏类型安全。
2. 使用通配符 ?
当不需要关心具体类型时,可以使用通配符:
public void printList(List<?> list) {for (Object obj : list) {System.out.println(obj);}
}
还可以结合上下限使用:
<? extends T>
:表示 T 或其子类<? super T>
:表示 T 或其父类
3. 泛型不能使用基本类型
只能使用包装类型,如 Integer
、Double
等,不能直接使用 int
、double
。
4. 类型擦除(Type Erasure)
Java 的泛型是通过类型擦除实现的,即在运行时泛型信息会被擦除:
List<String> list1 = new ArrayList<>();
List<Integer> list2 = new ArrayList<>();
System.out.println(list1.getClass() == list2.getClass()); // true
这意味着泛型只存在于编译阶段。
🧪 六、自定义泛型类示例
下面是一个简单的泛型容器类 Lhist<V>
:
public class Lhist<V> {private V[] array;private int size;public Lhist(int capacity) {array = (V[]) new Object[capacity];}public void add(V value) {if (size == array.length)throw new IndexOutOfBoundsException();array[size++] = value;}public V get(int index) {if (index >= size)throw new IndexOutOfBoundsException();return array[index];}public int size() {return size;}
}
使用方式:
Lhist<String> stringList = new Lhist<>(10);
stringList.add("Hello");
System.out.println(stringList.get(0)); // 输出 Hello
📦 七、Java 类库中的泛型应用
Java 标准库从 Java 5 开始全面支持泛型,尤其是集合框架:
Collection<E>
List<E>
Set<E>
Map<K,V>
Queue<E>
例如:
List<String> names = new ArrayList<>();
names.add("Tom");
String name = names.get(0); // 无需强转
还广泛使用了通配符和边界限定:
public boolean addAll(Collection<? extends E> c);
📘 八、总结
特点 | 描述 |
---|---|
类型安全 | 在编译期检查类型,避免运行时异常 |
消除强制类型转换 | 提高代码可读性和安全性 |
代码复用 | 一份逻辑适配多种类型 |
通配符 | 支持灵活的类型匹配(? , ? extends T , ? super T ) |
类型擦除 | 运行时无泛型信息,仅保留原始类型 |
📚 参考资料 / 推荐阅读
- Oracle 官方文档:Generics
- 《Effective Java》第 5 条:避免不必要的泛型使用
- 《Java 编程思想》泛型章节
如果你觉得这篇文章对你有帮助,欢迎点赞 + 收藏 + 关注我,后续将持续更新 Java 技术干货内容!