什么是泛型?
在我们jdk1.5之前,我们一般是用到什么类型的字段就给什么数据类型的,如一般情况下我们使用一个i变量,就赋予它int类型,这样i这个变量就只能使用整型类型,如果使用其它类型将会报错。
但是这样,1.第一个就不符合我们Java面向对象的思想了,就是说人家需要什么,你就给人家造什么,这是一种面向过程的思想。
2.第二就是我们在使用的过程中不确定使用什么类型的,就想着在用户创建对象的时候确定什么类型了才决定,
3.第三个就是指定类型有的时候编译期是不会指出使用类型错误的,我们也希望编译期可以检查创建的类型是否错误。
按照原来面向过程的做法就不可能做到,并出现以上问题。
其实最主要是第一个原因,面向对象的问题,在这个类的基础上创建一个对象,一个对象具体的性格特征这些我们是不可以掌握的,但是一个类就不是了,它是提供创建对象的过程中可以选择哪一种性格给这个对象(eg:女娲娘娘在造人的时候,并不知道一个人具体的性格特征,为了造人方便就创造了很多个类,女娲娘娘就可以按照这些类选择哪一种性格给这个具体的人然后生成就可以了,造人的过程中只只要按照类的功能选择给人就可以快速造人了)。而我们的类型也应该一样,符合面向对象的特征,这个类型应该在我们“造”对象的时候由用户决定而不是已经定死了。
因此,我们的大佬们就创造出了泛型,就是指在类定义时不会设置类中的属性或方法参数的具体类型,而是在类使用时(创建对象)再进行类型的定义。
如ArrayList<String> list=new ArrayList<String>();
list.add("我是靓仔");//编译通过,是string类型
list.add(100);//编译不通过,是Integer类型
而且我们也可以创建另一个Integer的对象,ArrayList<Integer> list1=new ArrayList<Integer>();这样list1就是可以根据自己想要哪个类型的去创建的。
为什么使用泛型?
上面我们说到了,主要三点
1.符合Java面向对象的特征
2.泛型可以让我们创建对象的时候自己决定类型,代码也可以易拓展,减少代码冗余。
3.可以在我们编译期就可以知道类型使用正不正确。
Java是怎么实现泛型的?
真泛型
在我们的世界里,看到了使用泛型知道这个类型没有确定,等我们创建对象的时候再确定,但是计算机可不是这么认为的,计算机是不允许犹犹豫豫的,是什么就是什么,那我们就希望把类型也封装成一个类里面,等创建的时候用到哪个就是哪个,也就是“真泛型”的操作方法,那这种方法可行吗?
这种“真泛型”的方法会产生两个比较麻烦的问题:1.我们jdk1.5之前是没有泛型这一个概念,那得修改JVM源码,让 JVM 能正确的的读取和校验泛型信息,这样才可以正确的符合“真泛型”的做法;2.为了兼容老程序,需为原本不支持泛型的 API 平行添加一套泛型 API(主要是容器类型)也就是说我们jdk1.5以前可是开发了很多的工作的,如果要按“真泛型”那1.5之后的和1.5之前的就不会兼容了。也就是说,比如我们源码是显示java.util.ArrayList,如果按照“真泛型”的方法,源码要更改为:java.util.generic.ArrayList<T>,
但是因为要更改的类型并不止一个,是有很多个的,这种工作相信大家也可以想象开发商会是多痛苦,还有还不能使用1.5之前的代码了,因为不兼容:
借图:
这个工作很累,以前的类型都要更改,很麻烦
这个就是不兼容,“真泛型”导致1.5之前的和1.5之后的互不能兼容
伪泛型
但是开发商这种都是大佬级别的,肯定是不会这么简单粗暴的了,os:“我这种大佬,傻逼才给它这样加班加点”,因此就要解决两个问题:
1.我不想加班加点改源码,但是我又要实现泛型
2.而且实现了泛型之后还得解决版本不兼容,我得承认我之前的jdk是有用了,我是大佬!!!
后来大佬们就想到了办法,做以下两件事:
1.那我就不新增一套泛型API,老子绝对不干这种事,那我直接把原来已有的泛型原地泛型化
2.处理好泛型前后类型的兼容
思来想去还是觉得骗老板比较好,做一个假的泛型化,即伪泛型,看起来像是有泛型的样子,实际上根本就没有在源码上实现泛型API,即在源码上做泛型擦除。(我也是逼不得已的)
以ArrayList为例,第一件事比较好处理,就是直接在原有源码中修改就好——
//👇Java老版本
class ArrayList{}
//👇Java5泛型版本
class ArrayList<T>{}
第二件事,就比较麻烦,jdk1.5之后的引用了jdk1.5之前的ArrayList,即泛型化的ArrayList<T>传递给了老版本的ArrayList,ArrayList list = new ArrayList<String>();那怎么保证泛型前后的ArrayList等价呢?
使用“真泛型”我们知道,泛型化的ArrayList list根本就没有指定哪个类型的,肯定不可能和ArrayList@String@不是一个类的,这是一个大问题!!!
大佬们就想到了,骗老板,让泛型化在编译过程中看起来是创造了新类,与我们没有泛型化的是同一个类,但是实际上根本就没有为参数化类型创造新类,即将泛型擦除,编译期间的泛型类型的类型参数全部替换为Object,也就是说泛型类中的T,在编译之后都会变成Object——
为什么擦除泛型类型变成Object呢?
这个是因为Object是所有类型的父类,是类是祖宗,我们知道Java是可以向上转型的,比如Integer是Number的子类,那么使用可以是一个Integer类就是Number类(如广州是广东的),当然反过来就不好了,因此任何类都是Object类
当然我不想将类型擦除变成Object怎么办,我只想让这个类是String类型的,不想它还可以是Integer类型的,这样应该怎么办?聪明的大佬们其实都想到了大家的想法了——
通配符
<?> 一般用在方法参数中,表示可以接受该类所有类型的泛型变量。
上限通配符
<? extends 类> 表示?可以指代任何类型,但是该类型必须是后面类的子类。
当然使用的过程中,只能是Number本身,或者Number类的子类,其它类型的都会报错
下限通配符
<? super 类> 此时?表示可以指代任意类型,但是该类型必须是后面类的父类。
此时表示?必须是String及其父类,所有此时?只能指代String或Object。即使用过程中只能是自己本身或者自己的父类,其它类型的都会报错。