Java 泛型是 Java SE 5 引入的一种特性,允许在编写代码时指定类、接口或方法的类型参数。通过泛型,你可以编写更具通用性、类型安全的代码,避免在运行时遇到不必要的类型转换错误。
1. 泛型的基本语法
泛型的基本形式如下:
class Box<T> {private T t;public void set(T t) { this.t = t; }public T get() { return t; }
}
这里,T
是一个类型参数,可以被任何类型替代。当你实例化 Box
类时,你可以指定 T
是什么类型:
Box<Integer> integerBox = new Box<>();
Box<String> stringBox = new Box<>();
2. 泛型类
泛型类是将类中的某个或多个属性设为泛型类型。例如,ArrayList<E>
就是一个常见的泛型类,其中 E
代表集合中元素的类型。
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<Integer, String> pair = new Pair<>(1, "Apple");
3. 泛型方法
泛型方法允许在方法内部定义类型参数,这些类型参数的作用范围仅限于该方法。泛型方法的声明方式是在返回类型前面加上 <T>
。
public <T> void printArray(T[] array) {for (T element : array) {System.out.println(element);}
}
你可以这样调用这个方法:
Integer[] intArray = { 1, 2, 3 };
String[] stringArray = { "Hello", "World" };
printArray(intArray);
printArray(stringArray);
4. 有界类型参数
有时候你希望泛型类型参数是某种类型的子类(或实现某接口),这时可以使用有界类型参数。语法是 <T extends SomeClass>
。
public <T extends Number> void processNumbers(T[] numbers) {for (T number : numbers) {System.out.println(number.doubleValue());}
}
此方法仅接受 Number
类及其子类的数组。
5. 通配符
通配符 ?
表示未知的类型,它在以下情况下非常有用:
7. 注意事项
总结
通过掌握 Java 泛型的基本概念和实际应用,开发者能够编写更灵活、复用性更高且类型安全的代码。这不仅提高了代码的可维护性,还能在编译时避免许多潜在的错误。
- 无界通配符
<?>
:可以接受任何类型。 - 有界通配符
<? extends T>
:类型必须是T
或T
的子类。 - 下界通配符
<? super T>
:类型必须是T
或T
的超类。public void printList(List<?> list) {for (Object elem : list) {System.out.println(elem);} }
6. 泛型在实际项目中的应用
泛型主要用于以下几个方面,提升代码的灵活性和安全性:
-
集合框架:Java 的集合框架广泛使用了泛型,像
List<T>
、Map<K,V>
等,使得集合中的元素类型在编译期就得到检查,避免了运行时的类型转换错误。 -
类型安全的API:通过泛型,你可以创建类型安全的 API。例如,开发者可以利用泛型来编写通用的数据处理方法,而不必为每种数据类型单独编写处理方法。
-
复用性高的代码:泛型可以让你编写更加通用和复用性高的代码。例如,排序算法可以使用泛型来对不同类型的数组进行排序,而不需要针对每种类型单独实现。
- 类型擦除:Java 的泛型是通过类型擦除实现的,即在编译时,所有的泛型信息都会被移除,这意味着运行时无法获得泛型参数的类型。
- 原始类型:避免使用原始类型(如
List
而非List<String>
),因为它会导致类型安全问题。 - 实例化泛型类型:无法直接实例化泛型类型(如
new T()
),因为在运行时无法确定T
的具体类型。