什么是泛型
泛型(Generics)是一种编程语言特性,它使得类或方法可以在定义时使用一个或多个类型参数,从而实现代码的通用性和类型安全性。泛型允许在编写类或方法时不指定具体的类型,而是在实际使用时指定类型参数,这样可以编写出更加通用和灵活的代码。
使用泛型的好处
代码重用:使用泛型可以编写出与特定数据类型无关的通用算法和数据结构,从而提高代码的重用性。
类型安全:通过在编译时进行类型检查,可以避免在运行时出现类型转换错误,提高程序的健壮性和安全性。
减少重复代码:在没有泛型的情况下,可能需要为每种数据类型都编写相似的代码,而使用泛型可以减少重复的代码量。
泛型如何使用
- 使用泛型的集合接口和类
// 使用泛型的 List 接口和 ArrayList 类
import java.util.List;
import java.util.ArrayList;
public class GenericListExample {
public static void main(String[] args) {
List<String> stringList = new ArrayList<>();
stringList.add("Hello");
stringList.add("World");
List<Integer> intList = new ArrayList<>();
intList.add(123);
intList.add(456);
System.out.println("String list: " + stringList);
System.out.println("Integer list: " + intList);
}
}
- 使用泛型的集合方法
// 使用泛型的 Collection 接口方法
import java.util.ArrayList;
import java.util.Collection;
public class GenericCollectionMethods {
public static void main(String[] args) {
Collection<String> stringCollection = new ArrayList<>();
stringCollection.add("Apple");
stringCollection.add("Banana");
Collection<Integer> intCollection = new ArrayList<>();
intCollection.add(10);
intCollection.add(20);
// 使用泛型的 size() 方法获取集合大小
System.out.println("String collection size: " + stringCollection.size());
System.out.println("Integer collection size: " + intCollection.size());
// 使用泛型的 contains() 方法检查元素是否存在
System.out.println("Contains 'Banana': " + stringCollection.contains("Banana"));
System.out.println("Contains 30: " + intCollection.contains(30));
}
}
- 使用泛型的 Map 接口和 HashMap 类
// 使用泛型的 Map 接口和 HashMap 类
import java.util.Map;
import java.util.HashMap;
public class GenericMapExample {
public static void main(String[] args) {
Map<String, Double> priceMap = new HashMap<>();
priceMap.put("Apple", 2.5);
priceMap.put("Banana", 1.0);
System.out.println("Price of Apple: " + priceMap.get("Apple"));
System.out.println("Price of Banana: " + priceMap.get("Banana"));
}
}
泛型的继承
在泛型中,子类可以是泛型类,也可以是非泛型类。当子类是泛型类时,可以通过指定类型参数来继承父类的泛型类型
// 泛型的继承示例
class Box<T> {
private T t;
public void set(T t) { this.t = t; }
public T get() { return t; }
}
// 继承泛型类的子类
class BoxSubclass<T> extends Box<T> {
// 子类可以保留父类的泛型类型
}
// 继承非泛型类的子类
class NonGenericBoxSubclass extends Box<String> {
// 子类可以指定具体的泛型类型
}
通配符
通配符使得能够以一种灵活的方式处理泛型类型。在 Java 中,通配符使用?表示,有两种通配符:? extends T 和 ? super
T,分别表示某个类型的子类型和父类型。
使用 ? extends T:
表示通配符所代表的类型是 T 的子类型(包括 T 本身),在使用时可以安全地获取类型为 T 的对象或其子类型的对象。
// 使用 ? extends T 的通配符
public void processElements(List<? extends Number> elements) {
for (Number n : elements) {
// 在这里可以安全地获取 Number 或其子类的对象
}
}
使用 ? super T:
表示通配符所代表的类型是 T 的父类型(包括 T 本身),在使用时可以安全地向集合中添加类型为 T 的对象或其父类型的对象。
// 使用 ? super T 的通配符
public void addElement(List<? super Integer> list) {
list.add(10); // 可以安全地向集合中添加 Integer 或其父类的对象
}
泛型使用注意事项
类型擦除:Java 中的泛型是通过类型擦除(Type Erasure)实现的,编译器在编译时会将泛型类型擦除为原始类型。这意味着在运行时无法获取泛型的具体类型信息。因此,在泛型代码中无法使用基本类型作为类型参数,只能使用对象类型。
通配符限定:通配符(Wildcard)可以用来表示未知类型,通配符可以用于方法参数、泛型类的类型参数等。通配符有上界通配符(? extends T)和下界通配符(? super T),在使用通配符时需要注意通配符的限定条件。
类型转换和 instanceof 操作符:由于类型擦除的存在,无法直接创建泛型类型的实例。在某些情况下,可能需要进行类型转换,但要谨慎处理类型转换操作,以避免出现 ClassCastException 异常。同时,由于类型擦除的原因,不能使用 instanceof 操作符检查泛型类型。
泛型数组:在 Java 中不能直接创建泛型数组,如 List[] array = new List[10]; 是非法的。可以使用泛型集合代替泛型数组的使用。
泛型方法:除了泛型类,Java 还支持泛型方法。在编写泛型方法时,需要注意泛型方法的定义和使用方式,以确保正确传递类型参数。
擦除后的限制:由于类型擦除的存在,可能会导致一些限制,如不能直接创建泛型类型的实例、不能使用基本类型作为类型参数等。在编写泛型代码时,需要考虑到这些限制。