什么是泛型
Java 泛型(generics)是 JDK 5 中引入的一个新特性, 泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型。
泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。
假定我们有这样一个需求:写一个排序方法,能够对整型数组、字符串数组甚至其他任何类型的数组进行排序,该如何实现?
答案是可以使用 Java 泛型。
使用 Java 泛型的概念,我们可以写一个泛型方法来对一个对象数组排序。然后,调用该泛型方法来对整型数组、浮点数数组、字符串数组等进行排序。
泛型的写法
一般我们会有以下两种写法
<T extends InterfaceName <T>>和<T extends InterfaceName <? super T>>
InterfaceName 是泛型要实现的接口,一般来说我们使用第一种方法声明泛型就可以满足需求.
class Animal implements Comparable<Animal>{
protected int age;
public Animal(int age){
this.age = age;
}
public void move() {
System.out.println("动物可以移动");
}
@Override
public int compareTo(@NonNull Animal animal) {
return this.age-animal.age;
}
}
class Main {
public static <T extends Comparable<T>> void mySort1(List<T> list){
Collections.sort(list);
}
public static <T extends Comparable<? super T>> void mySort2(List<T> list){
Collections.sort(list);
}
public static void main(String[] args) {
List<Animal> animals = new ArrayList<>();
animals.add(new Animal(2));
animals.add(new Animal(1));
mySort1(animals);
mySort2(animals);
}
}
比如上边这段代码,声明两个排序方法,分别使用上述两种方式声明泛型变量.
Animal实现了Comparable接口所以无论使用哪种声明泛型变量的排序方法都可以调用.看不出两种声明方式有什么区别.
下边看这段代码
class Animal implements Comparable<Animal>{
protected int age;
public Animal(int age){
this.age = age;
}
public void move() {
System.out.println("动物可以移动");
}
@Override
public int compareTo(@NonNull Animal animal) {
return this.age-animal.age;
}
}
//增加Dog 继承与 Animal
class Dog extends Animal {
public Dog(int ageInt){
super(ageInt);
}
public void move() {
System.out.println("狗可以跑和走");
}
public void bark() {
System.out.println("狗可以吠叫");
}
}
class Main {
public static <T extends Comparable<T>> void mySort1(List<T> list){
Collections.sort(list);
}
public static <T extends Comparable<? super T>> void mySort2(List<T> list){
Collections.sort(list);
}
public static void main(String[] args) {
List<Animal> animals = new ArrayList<>();
animals.add(new Animal(2));
animals.add(new Animal(1));
mySort1(animals);
mySort2(animals);
List<Dog> dogs = new ArrayList<>();
dogs.add(new Dog(4));
dogs.add(new Dog(3));
mySort1(dogs);
mySort2(dogs);
}
}
好,如果执行这段代码java就会稳稳的报错了.
错误: 无法将类 Main中的方法 mySort1应用到给定类型;
需要: List<T>
找到: List<Dog>
原因: 推断类型不符合上限
推断: Dog
上限: Comparable<Dog>
其中, T是类型变量:
T扩展已在方法 <T>mySort1(List<T>)中声明的Comparable<T>
可以看到,虽然Dog继承了Animal,并且它的父类实现了Comparable接口,然而并不可以通过编译,所以我们得到一个答案
<T extends Comparable<T>>这种声明泛型变量的方式,要求当前类本类必须实现接口,无关父类.
<T extends Comparable<? super T>>而这种方式可以通过编译,那么显然它比第一种方式更加灵活,只要它的父类实现了接口,那么它作为泛型入参也是可以满足条件的.
根据上面的小demo可以得出结论:
第一种写法简单但限制必须是当前类实现接口不够灵活,
第二种方法会查找根据继承关系去查找是否有父类实现过接口,有即满足泛型入参要求.更加灵活
大家可以根据具体场景选用泛型变量的声明方式