通俗易懂的理解JAVA泛型

泛型是JAVA 1.5版本之后引入的一个新特性,它允许在定义类、接口和方法的时候使用类型参数,这些类型参数将在使用时确定。泛型的引入,使得JAVA具有了更好的代码复用性,更好的类型安全性,更好的可读性。泛型的引入,使得JAVA具有了更好的代码复用性,更好的类型安全性,更好的可读性。

JAVA泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。这种参数类型可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口、泛型方法。

JAVA泛型的好处:

  1. 代码复用性:泛型类、泛型接口、泛型方法的代码可以被重用,不用重复编写相同的代码。
  2. 类型安全:泛型可以在编译时期进行类型检查,保证类型安全。
  3. 可读性:泛型可以减少强制类型转换的代码,提高代码的可读性。

以动物界为例解释多态和泛型

什么是多态

多态就是同一种事物的多种形态,比如动物,狗,鸟,他们都是动物,但是他们的运动方式不同,这就是多态
不同的动物的运动方式不同,比如狗的运动方式是跑,鸟的运动方式是飞,那么我们可以定义一个动物类,然后定义一个狗类和鸟类,他们同属于动物,却又有不同的运动方法,这就实现了多态

什么是泛型

虽然同属于动物界,但是动物们的繁衍行为生下的后代也不相同 ,比如狗生下的后代是狗,鸟生下的后代是鸟 ,如果我们用一个类来表示动物,且定义一个繁衍行为返回后代

1
2
3
4
5
public class Animal {
public Animal breed(){
return new Animal();
}
}

如果我希望对某个具体的动物进行描述,就只能继承动物类,然后重写breed方法,比如狗类

1
2
3
4
5
6
public class Dog extends Animal{
@Override
public Dog breed(){
return new Dog();
}
}

那有没有一种方式 可以在我指定的动物是狗的时候 直接生出一只狗狗呢?这就需要用到泛型了

我们可以定义一个占位符 T ,然后在breed方法中返回一个T类型的对象

1
2
3
4
5
public class Animal<T> {
public T breed(){
return new T();
}
}

使用的时候 只需要替换T为Dog 就可以了,这样当我需要获取狗的后代的时候,我就可以这样写

1
2
Animal<Dog> dog = new Animal<>();
Dog dog1 = dog.breed();

这个T 可以是任何类型,比如狗,鸟,猫, 此时Animal 就是一个泛型类,可以用来描述任何动物,而不用每次都去继承一个动物类,然后重写breed方法,这样就可以大大的减少代码量,提高代码的复用性

类型安全

如果说现在很多园区,每个园区都需要放入对应的动物,狮子区不能放入兔子,不然狮子就会吃掉兔子,那么我们就可以定义一个园区类,然后定义一个放入动物的方法,这个方法的参数就是一个泛型类

1
2
3
4
5
6
public class Zoo<T extends Animal> {
public void put(T animal){
System.out.println("放入了一个"+animal.getClass().getSimpleName());
}
}

这样就可以保证放入的动物是对应的,这样就可以保证类型安全

放入兔子

1
2
Zoo<Rabbit> rabbitZoo = new Zoo<>();
rabbitZoo.put(new Rabbit());

放入狮子

1
2
Zoo<Lion> lionZoo = new Zoo<>();
lionZoo.put(new Lion());

如果放入了一个错误对象,比如放入了一只狗,编译器就会报错

1
2
Zoo<Lion> lionZoo = new Zoo<>();
lionZoo.put(new Dog());

泛型的使用细节

类型实参不能是基本数据类型,比如int,boolean,但是可以是他们的包装类,比如Integer,Boolean

1
2
Zoo<Integer> integerZoo = new Zoo<>();
integerZoo.put(new Integer(1));

静态方法不能使用泛型类中定义的泛型,因为静态方法优先于对象存在,所以静态方法中不能使用类中定义的泛型

1
2
3
4
5
6
public class Zoo<T extends Animal> {
public static void put(T animal){ //这里会出现错误,因为静态方法不能使用泛型类中定义的泛型

System.out.println("放入了一个"+animal.getClass().getSimpleName());
}
}

修饰符与返回值类型中间的 泛型标识符 <T,E,…>,是 泛型方法的标志

吧v

1
2
3
4
5
6
7
8
9
10
11
public class Zoo<T extends Animal> {
public void put(T animal){
System.out.println("放入了一个"+animal.getClass().getSimpleName());
}
public <T> void put2(T animal){ //这里的T和泛型类中的T没有任何关系,这里的T是一个新的类型,也可以写成B ,C ,D等等 但是需要再修饰符后面声明
System.out.println("放入了一个"+animal.getClass().getSimpleName());
}
public <T,B,C,D,E> void put2(T animal,B b,C c,D d){ //这里的T和泛型类中的T没有任何关系,这里的T是一个新的类型,也可以写成B ,C ,D等等 但是需要再修饰符后面声明
//....
}
}

泛型中的extends 和 super

extends 表示上界,只能是指定类型或者指定类型的子类
super 表示下界,只能是指定类型或者指定类型的父类

1
2
3
4
5
public class Zoo<T extends Animal> {
public void put(T animal){
System.out.println("放入了一个"+animal.getClass().getSimpleName());
}
}

这里的T 只能是Animal或者Animal的子类,如果我想要放入Animal的父类,比如Object,就需要使用super

1
2
3
4
5
public class Zoo<T super Animal> {
public void put(T animal){
System.out.println("放入了一个"+animal.getClass().getSimpleName());
}
}

这里的T 只能是Animal或者Animal的父类,如果我想要放入Animal的子类,比如Dog,就需要使用extends