1.泛型
2.泛型--统一数据类型
如下图,当我们在泛型中添加不同的数据类型,add方法需要的数据类型也随之改变
[1]
[2]
泛型--默认类型object
当我们不指定泛型时,泛型的默认类型为object,所以add方法可以存储任意数据类型
3.泛型--将运行期间错误提升到编译期
如下图,我们在集合中添加了一些元素,使用迭代器遍历集合,然后想调用length方法查看集合长度,发现报错
报错的原因,在迭代器获取元素的那一行,左边是父类引用object,右边是子类
编译期间先看父类有没有length,再调用子类逻辑,很明显object类是不存在length这个方法的,所以报错
解决的方法很简单,使用向下转型
接着,我们右键运行程序,控制台打印的都是2,没有问题
但是,在object类型向下转型为String时没有报错,是因为我们添加的都是字符串类型,如果集合中存储了其他数据呢?如下图
我们将集合的泛型删去,可以添加任意的数据类型变量,此时我们添加一个随机数对象,发现程序没有报错
此时,我们右键进行运行,控制台出现了类型转换异常
异常的原因也能想清楚,随机数对象不可以向下转型为字符串
我们仔细想想,如果我们在集合中加了String泛型,那这个异常还会保留到运行期吗?
添加了泛型后,便会在编译期间报错,完全到不了运行期
这就是泛型将运行期错误提升到了编译期
4.泛型的学习路径
常见的泛型标识符
泛型标识符就是<>里面的字母,当个提示来看,没什么特殊含义
泛型的学习路径中,我们还需要学习一点,如下图
1.泛型类:创建对象的时候指定泛型
集合中可以指定泛型,我们自己当然也可以指定泛型,如下图,我们自己写一个类然后指定一个泛型
但是指定了这个泛型有什么用呢?
--假如我们要写一个变量或是方法,但是当时没想到要用什么类型,就可以使用泛型,等到创建类的时候再进行具体类型指定
2.泛型方法
泛型方法分为两种,一种非静态,一种静态
它们指定泛型的时机,如下图
[1]非静态方法
我们刚才在说泛型类的时候就使用到了泛型方法中的非静态方法,比如setE
泛型类是什么泛型,非静态方法的泛型也就跟着是什么类型
[2]静态方法
如下图,我们写了一个方法,然后使用不同数据类型的数组进行调用
报错的原因很简单,其他两个数组的数据类型和方法形参类型不匹配,我们就会发现这个方法局限性很大,不够灵活
那如果我们想让这个方法更加灵活该怎么做呢?--在方法中添加泛型
如图,我们在静态方法中指定泛型,这时方法就可以根据传入的数组来确定形参的类型,从而让该方法可以接收任意类型的数组
如上图,静态方法报错,这是因为静态方法不知道自己现在是什么类型
那之前说了方法跟着类走,我们试着在类上也指定个泛型能否有效呢?
如上图,静态方法还在报错,这是为什么呢?
想一想,类的泛型是什么时候确定的?--创建类的时候
而静态修饰的方法是优先于对象存在的,所以这个静态方法存在时,类还确定不到具体类型,当然不能根据类来指定静态方法的泛型了
既然不能跟着类混,那静态方法就要自己独自声明泛型,如下图,不再报错
静态方法确定泛型的时机:调用静态方法时
之前说过,泛型是只能指定引用数据类型,所以所有的基本数据类型都无法通过泛型修饰
3.泛型接口
1.泛型接口:在实现类实现接口时,指定泛型
假如,我们写了一个接口,里面的方法无法直接确定类型,就可以使用泛型,等到类实现接口时,再指定实际类型
2.泛型接口:实现类也声明泛型,等到创建对象时再确定实际类型
我们写了一个接口,无法确定具体类型,所以声明了一个接口,当实现类去实现泛型接口时,它也无法确定是什么类型,那就继续声明泛型
..
当创建这个实现类时,再确定具体类型
4.泛型通配符
当我们写了一个方法,想让这个方法能接收任意类型的集合时,我们就可以使用泛型通配符
如图,method既能接收Coder类型的集合,也可以接收Manager类型的集合
当method接收了集合后,遍历集合,取出存在集合里的coder和manager对象,可以看见接收它们的变量是object类型
此时,object接收了它们,如果要调用work方法会报错,因为object类中不存在,所以要向下转型
为了确保这两个对象都能使用,我们使用它们的父类employee做向下转型
右键运行,没有问题
但是,该代码还存在安全隐患,泛型通配符是任意类型的集合都可以接,这样就极容易和向下转型产生类型转换异常 如图
我们希望泛型通配符只接收employee这个体系里的对象,不是这个体系的对象不进行接收 如下图
此时,String类型的list3就无法传入method