首页 > 代码库 > 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泛型的实现原理