泛型(Generics) 是 Java 中的一项强大特性,允许你在类、接口和方法中使用类型参数,而不是指定具体的类型。泛型的主要作用是增强代码的 类型安全 和 可重用性。
泛型的定义:
泛型允许你在编写代码时使用占位符类型(通常是字母,如 T
、E
、K
、V
等),在实际使用时才指定具体的类型。通过使用泛型,你可以编写更加通用、灵活和类型安全的代码。
泛型的作用:
-
类型安全:
- 泛型可以在编译时进行类型检查,避免了运行时的类型转换异常。
- 在没有泛型的情况下,我们往往需要进行显式的类型转换,这可能导致类型错误,而泛型则能保证数据类型的正确性。
举个例子:
// 使用泛型,集合类型安全 List<String> list = new ArrayList<>(); list.add("Hello"); // list.add(10); // 编译错误,类型不匹配 // 不使用泛型,可能出现类型转换异常 List list = new ArrayList(); list.add("Hello"); list.add(10); String str = (String) list.get(0); // 类型转换错误可能发生
-
代码重用:
- 泛型允许你编写可以处理不同类型的代码,避免了重复编写相同逻辑的代码。通过泛型,你可以编写通用的算法和数据结构,应用于多种不同的数据类型。
示例:
public class Box<T> { // T 是泛型类型 private T value; public void setValue(T value) { this.value = value; } public T getValue() { return value; } } // 使用 Box 存储不同类型的值 Box<String> box1 = new Box<>(); box1.setValue("Hello"); Box<Integer> box2 = new Box<>(); box2.setValue(100);
-
避免类型强制转换:
- 使用泛型可以减少强制类型转换的需求。在没有泛型时,集合中的元素会被当作
Object
类型来处理,取出来时需要手动强制转换。使用泛型后,编译器会自动检查类型,无需手动转换。
- 使用泛型可以减少强制类型转换的需求。在没有泛型时,集合中的元素会被当作
-
增强可读性:
- 泛型使得代码的意图更加明确,能让其他开发人员一眼看出这个类、接口或方法所操作的数据类型。
泛型的常见用法:
-
泛型类:
- 在类定义时使用泛型,这样该类就能处理多种类型的数据。
class Box<T> { private T value; public void setValue(T value) { this.value = value; } public T getValue() { return value; } }
-
泛型方法:
- 方法中的泛型独立于类的泛型,可以在方法中使用类型参数。
public <T> void printArray(T[] array) { for (T element : array) { System.out.println(element); } }
-
泛型接口:
- 接口可以使用泛型,适用于希望在不同实现中使用不同类型的场景。
interface Pair<K, V> { K getKey(); V getValue(); }
-
通配符(Wildcard) :
- 通配符
?
代表未知类型,常用于方法参数中,允许传入不同类型的泛型。 ? extends T
:表示类型是T
或T
的子类。? super T
:表示类型是T
或T
的父类。?
:表示任意类型。
示例:
public void printList(List<?> list) { for (Object item : list) { System.out.println(item); } }
- 通配符
泛型的限制:
- 不能创建泛型类型的实例: 你不能使用泛型类型参数来创建数组或者实例化对象。例如,
new T()
是不允许的,因为编译时泛型类型参数未知。 - 不能使用泛型类型参数进行静态变量声明: 静态变量属于类本身,而泛型类型参数属于对象实例,所以静态变量不能使用泛型类型参数。