集合中的泛型是Java等编程语言中一种强大的特性,它允许在编译时期而不是运行时期检查集合中元素的类型。通过使用泛型,可以创建可以工作于任何类型的集合,同时保持类型安全。这意味着,当你尝试向集合中添加不兼容类型的元素时,编译器会报错,而不是在运行时抛出异常。
泛型的好处
- 类型安全:泛型集合可以确保你只能插入正确类型的对象,并在编译时检查类型错误。
- 消除强制类型转换:使用泛型后,从集合中取出的对象不需要再进行强制类型转换。
- 提高代码的重用性:你可以编写与类型无关的通用代码,这些代码可以用于多种数据类型。
泛型通配符
泛型通配符(?
)用于表示未知的类型。它可以作为类型参数使用,但只能用于读取集合中的元素,不能用于写入。泛型通配符有三种形式:
?
:未知类型,只读。? extends T
:未知类型,但它是T或T的子类,表示上限通配符,用于读取。? super T
:未知类型,但它是T或T的父类,表示下限通配符,用于写入。
泛型方法
泛型不仅可以用于类,还可以用于方法。泛型方法允许你定义一个方法,该方法可以操作多种类型的数据。在方法声明时,你可以在返回类型之前指定一个或多个类型参数。
- 当你不知道该方法要传递什么类型的参数时,可以使用泛型
- 用T表示一个要出入的未知类型参数
- 泛型方法有很好的复用性,此时使用不同类型的参数,就不需要再去创建别的方法了
public static <T> void printArray(T[] inputArray) {
for (T element : inputArray) {
System.out.printf("%s ", element);
}
System.out.println();
}
// 使用示例
Integer[] intArray = { 1, 2, 3, 4, 5 };
printArray(intArray);
String[] stringArray = { "Hello", "World" };
printArray(stringArray);
泛型类
泛型类在类名后紧跟一对尖括号<>
,并在尖括号中声明类型参数。类型参数是一种占位符,用于表示在实例化类时将用实际类型替换的类型。
public class Box<T> {
//T 代表“类型”——但应该使用有意义的名称,如 E、K、V 等。
private T t;
public void set(T t) {
this.t = t;
}
public T get() {
return t;
}
}
在这个例子中,
Box
是一个泛型类,它有一个类型参数T
。这意味着Box
类可以与任何类型一起工作,具体取决于实例化Box
时指定的类型。
泛型类的实例化
Box<Integer> integerBox = new Box<>();
integerBox.set(123);
Integer value = integerBox.get();
Box<String> stringBox = new Box<>();
stringBox.set("Hello");
String anotherValue = stringBox.get();
在这个例子中,分别实例化了
Box
的两个对象:integerBox
和stringBox
。我们指定了
Box
的类型参数分别为Integer
和String
。因此,
integerBox
只能存储Integer
类型的对象,而stringBox
只能存储String
类型的对象。
集合中的泛型
- 假设有一个函数 fun(Animal animal),如果我们传入一个Dog d 对象进去,编译器是不会报错的,这是多态的概念;
- 假设有一个函数 fun(Animal[] animals),如果我们传如一个Dog[] dogs数组进去,编译器也不会报错,这就是数组的协变性;
- 假设有一个函数 fun(List<Animal> animal), 如果我们传如一个List <Dog> dog 集合进去,编译器就会报错了,这就是集合泛型的不变性;
那么该怎么办呢?
我们可以将泛型改成这样 fun (List <? extends Animal> ),这样之后,当我们再 传入一个List <Dog> dog 集合进去,编译器就就不会报错了。也就是说可以传入包含Animal的子类的List了。
定义三个实体类
// 定义一个Animal类
class Animal {
void eat() {
System.out.println(" animal eats food.");
}
}
// 定义一个Dog类,继承自Animal
class Dog extends Animal {
void bark() {
System.out.println("Woof!");
}
}
// 定义一个Cat类,也继承自Animal
class Cat extends Animal {
void meow() {
System.out.println("Meow!");
}
}
实现集合泛型
public class Generic {
// 使用? extends Animal作为类型参数,表示这个方法可以接受任何Animal或Animal子类的List
public static void processAnimals(List<? extends Animal> animalList) {
for (Animal animal : animalList) {
animal.eat(); // 合法,因为所有Animal及其子类都有eat方法
// animal.bark(); // 非法,编译器不知道animalList具体是Dog还是其他Animal子类
}
}
public static void main(String[] args) {
List<Dog> dogs = new ArrayList<>();
dogs.add(new Dog());
List<Cat> cats = new ArrayList<>();
cats.add(new Cat());
// 由于processAnimals方法使用了? extends Animal,它可以接受dogs和cats
processAnimals(dogs);
processAnimals(cats);
}
}
在这个例子中,
processAnimals
方法接受一个List<? extends Animal>
类型的参数,这意味着它可以接受任何List<Animal>
、List<Dog>
、List<Cat>
等列表,只要这些列表中的元素类型是Animal
或其子类。