首页 > 代码库 > JAVA泛型的实现原理
JAVA泛型的实现原理
1.基本学过JAVA的人都知道一点泛型,明白常出现的位置和大概怎么使用。
在类上为:class 类名<T> {} 在方法上为:public <T> void 方法名 (T x){}
就不再赘述了。
2.泛型就是将类型变成了参数去传入,使得可以使用的类型多样化,进而实现解耦。
JAVA因为泛型是在1.5以后出现的,为了保持对以前版本的兼容,使用了擦除的方法实现泛型。所以比起C++等在使用上限制较多。
擦除是什么呢,实际上就是一定程度无视了类型参数T,直接从T所在的类开始向上T的父类去擦除(或者说转型?)比如:我调用
泛型方法,传了类型参数T进入方法内部,如果没在声明时做类似public T 方法名(T extends 某个父类 x){}这样的声明,在我的参
数T和x传进去后,JAVA就进行了向上类型的擦除,直接把参数x当做Object类来处理,而不是我传进去的T。如果我声明了向上的界
限T extends Father,那就会把参数x擦除到Father类,当做Father类来使用。
也就是说,在有泛型的任何类或方法的内部,它都无法知道自己的泛型参数,就像泛型参数没有传入类或方法里面一样。
那为何泛型参数会在类和方法上起作用?
根据《Thinking in JAVA》里描述,擦除和转型都是在边界上发生的,也就是说我们传进去的参数在进入类或方法的时候被擦除掉了,
但是在传出来的时候又被转型成了我们设置的T。
根据这一情况我们可知道,在泛型类或方法内,任何涉及到具体类型(即擦除后的类型的子类)的操作都不能进行。比如:
new T();或者T.play()(play为某子类的方法而不是擦除后的类的方法),又或者同时有T.method(T x),Tmethod(K x)
3.在泛型里如下的例子无法成立
List<Number> list = new List<Integer>();
因为在编译器看来List<Number>不是List<Integer>的父类(我也不知道具体原因,谁知道告诉我下),虽然在我们看来Number是
Integer的父类,那么他们组成的容器List应该有这种继承关系。
为了实现这种继承关系,我们可以使用通配符?。这样写就可以了List<? extends Number>,这个是声明了这个List的内部成员继承自
Number,确定了List的上界。那么我们就能执行List<? extends Number> list = new List<Integer>();了。
但是要注意,原本我们泛型是参数类型T,规定了类型(你输入的啥类型就规定的啥),所以我们在执行list.add()的时候只能输入
类型T的数据。在使用统配符后我们扩大了可选类型的范围,所以我们的操作也受到了限定,比如:list.add(),因为我们可以选的实
例化List太多了,只要是Number的子类就行,JAVA可不知道我们会实例化什么List,为了确保安全就统统不能add了。但是我们可以
list.get(),JAVA会把我们get的全都变成Number类型输出(里氏替换原则)。
有了上界就能有下界,List<? super Integer>,和上界相反。因为我们不知道我们会是哪个Integer的父类,我们无法get到任何东西,
但是我们可以add进去,只要add的是Integer和它的子类就行(还是里氏原则)。
而且我们能使用List<? extends T>这种做法来进一步增加我们的选择。
4.还有无界通配符<?>,根据以上推论就知道其作用和限制了,它与不加泛型的List相比在于强调它是规定了某类的List,而不是Object
类的List,只是我也不确定是什么类。
JAVA泛型的实现原理