Java 泛型介绍
泛型是 Java 5 引入的一种强大的特性,它允许在类、接口和方法中使用类型参数来提高代码的复用性和类型安全。泛型的目标是实现代码的通用性,同时能够保证类型的安全性,避免强制类型转换错误。
1. 泛型的基本概念
泛型允许你定义类、接口或方法时指定类型参数,这样可以在创建对象时指定具体的类型。泛型的核心思想是 "类型参数化",通过在定义时使用占位符来表示某种类型,实际使用时可以指定具体类型。
泛型的基本语法:
class ClassName<T> {
// T 代表类型参数,可以是任何类型
private T value;
public ClassName(T value) {
this.value = value;
}
public T getValue() {
return value;
}
}
说明:
T
是一个类型参数(通常使用T
、E
、K
等表示类型),它表示一种类型,可以是任何类型,T
只是一个占位符。
2. 泛型的用途
泛型的主要用途是实现类型安全和代码复用。具体来说,泛型提供了以下好处:
- 类型安全:编译时检查类型错误,避免了运行时
ClassCastException
。 - 代码复用:通过类型参数化,可以实现与特定类型无关的代码,增加代码的通用性。
3. 泛型类
泛型类是指定义时使用类型参数的类。类可以定义一个或多个类型参数。
泛型类示例:
// 定义一个泛型类 Box,T 表示类型参数
class Box<T> {
private T value;
public void setValue(T value) {
this.value = value;
}
public T getValue() {
return value;
}
}
public class GenericClassExample {
public static void main(String[] args) {
Box<Integer> intBox = new Box<>();
intBox.setValue(100);
System.out.println(intBox.getValue()); // 输出: 100
Box<String> strBox = new Box<>();
strBox.setValue("Hello, Generic!");
System.out.println(strBox.getValue()); // 输出: Hello, Generic!
}
}
说明:
Box<T>
是一个泛型类,可以在实例化时指定类型,如Box<Integer>
或Box<String>
。- 泛型类的实例化时,
T
被替换为实际的类型。
4. 泛型方法
除了泛型类,Java 还允许在方法中使用泛型。泛型方法是指方法中带有类型参数,方法的返回类型或参数类型是未知的,直到调用方法时才指定具体类型。
泛型方法示例:
public class GenericMethodExample {
// 定义一个泛型方法,返回类型和参数类型均是泛型
public static <T> void printArray(T[] array) {
for (T element : array) {
System.out.println(element);
}
}
public static void main(String[] args) {
Integer[] intArray = {1, 2, 3, 4};
String[] strArray = {"Hello", "World"};
printArray(intArray); // 输出: 1 2 3 4
printArray(strArray); // 输出: Hello World
}
}
说明:
<T>
在方法签名中声明了泛型类型参数,表示该方法接受任意类型的数组,并打印出每个元素。- 泛型方法可以单独声明泛型,和类的泛型无关。
5. 泛型接口
接口也可以使用泛型参数。与泛型类类似,接口的泛型类型在实现该接口时会指定。
泛型接口示例:
// 定义一个泛型接口
interface Pair<K, V> {
K getKey();
V getValue();
}
class MyPair<K, V> implements Pair<K, V> {
private K key;
private V value;
public MyPair(K key, V value) {
this.key = key;
this.value = value;
}
public K getKey() {
return key;
}
public V getValue() {
return value;
}
}
public class GenericInterfaceExample {
public static void main(String[] args) {
MyPair<Integer, String> pair = new MyPair<>(1, "One");
System.out.println("Key: " + pair.getKey() + ", Value: " + pair.getValue()); // 输出: Key: 1, Value: One
}
}
说明:
Pair<K, V>
是一个泛型接口,定义了两个类型参数K
和V
,用于表示键值对。MyPair<K, V>
实现了这个接口,并提供了具体的实现。
6. 通配符(Wildcard)
在使用泛型时,有时我们并不关心具体类型,而只关心某个类型的上界或下界。此时可以使用通配符 ?
。
通配符的类型:
- 无上下界的通配符:
<?>
,表示可以是任何类型。 - 上界通配符:
<? extends T>
,表示T
类型及其子类型。 - 下界通配符:
<? super T>
,表示T
类型及其父类型。
通配符示例:
import java.util.*;
public class WildcardExample {
// 使用无上下界的通配符
public static void printList(List<?> list) {
for (Object obj : list) {
System.out.println(obj);
}
}
// 使用上界通配符
public static void printNumbers(List<? extends Number> list) {
for (Number num : list) {
System.out.println(num);
}
}
// 使用下界通配符
public static void addNumbers(List<? super Integer> list) {
list.add(1);
list.add(2);
System.out.println(list);
}
public static void main(String[] args) {
List<Integer> intList = new ArrayList<>();
intList.add(10);
intList.add(20);
List<Double> doubleList = new ArrayList<>();
doubleList.add(10.5);
doubleList.add(20.5);
// 无上下界通配符
printList(intList); // 输出: 10 20
printList(doubleList); // 输出: 10.5 20.5
// 上界通配符
printNumbers(intList); // 输出: 10 20
printNumbers(doubleList); // 输出: 10.5 20.5
// 下界通配符
List<Number> numList = new ArrayList<>();
addNumbers(numList); // 输出: [1, 2]
}
}
说明:
printList(List<?>)
:接受任何类型的List
。printNumbers(List<? extends Number>)
:接受Number
类型或其子类类型的List
,如Integer
或Double
。addNumbers(List<? super Integer>)
:接受Integer
类型或其父类类型的List
,可以向该列表中添加Integer
类型或其子类。
7. 泛型的边界
- 上界通配符(
extends
):指定泛型类型的上限,表示该类型必须是某个类型的子类。 - 下界通配符(
super
):指定泛型类型的下限,表示该类型必须是某个类型的父类。
边界示例:
// 上界通配符:传入的类型必须是 Number 或其子类
public static void printNumbers(List<? extends Number> list) {
for (Number num : list) {
System.out.println(num);
}
}
// 下界通配符:传入的类型必须是 Integer 或其父类
public static void addNumbers(List<? super Integer> list) {
list.add(1);
list.add(2);
System.out.println(list);
}
8. 泛型的限制
- 不能创建泛型数组:例如,
new T[10]
是不允许的。 - 不能使用泛型类型的
new
运算符:由于 Java 在编译时会进行类型擦除,无法知道实际的类型。 - 不能创建泛型类型的静态字段:因为泛型类型的信息会被擦除,不能用于静态字段。
9. 总结
类型参数 | 类定义时指定类型参数 T |
方法签名时指定类型参数 <T> |
? 表示任何类型,? extends T 表示 T 的子类型,? super T 表示 T 的父类型 |
用途 | 使类能处理不同类型的数据 | 使方法能够接受不同类型的参数 | 灵活处理不同类型的通配符 |
使用场景 | 创建通用类和集合类 | 创建通用的算法方法 | 处理不确定类型,限制类型的上下界 |
建议:
- 使用泛型类和方法时,确保尽可能泛化代码,增加代码的复用性。
- 使用通配符时要小心,避免引入类型不安全的问题。
Java碎碎念 文章被收录于专栏
来一杯咖啡,聊聊Java的碎碎念呀