封装设置属性,一家人都要整整齐齐系列(1) JAVA泛型的实现原理

1.基本学过JAVA的人都知道一点泛型,明白常出现的位置和大概怎么使用。在类上为:class 类名<T> {}  在方法上为:public <T> void 方法名 (T x){}就不再赘述了。

  2.泛型就是将类型变成了参数去传入,使得可以使用的类型多样化,进而实现解耦。JAVA因为泛型是在1.5以后出现的,为了保持对以前版本的兼容,使用了擦除的方法实现泛型。所以比起C++等在使用上限制较多。擦除是什么呢,实际上就是一定程度无视了类型参数T,直接从T所在的类开始向上T的父类去擦除(或者说转型?)比如:我调用泛型方法,传了类型参数T进入方法内部,如果没在声明时做类似public T 方法名(T extends 某个父类   t){}这样的声明,在我的参数T和t传进去后,JAVA就进行了向上类型的擦除,直接把参数t当做Object类来处理,而不是我传进去的T。如果我声明了向上的界 限T extends Father,那就会把参数t擦除到Father类,当做Father类来使用。 也就是说,在有泛型的任何类或方法的内部,它都无法知道自己的泛型参数,就像泛型参数没有传入类或方法里面一样。 那为何泛型参数会在类和方法上起作用? 根据《Thinking in JAVA》里描述,擦除和转型都是在边界上发生的,也就是说我们传进去的参数在进入类或方法的时候被擦除掉了, 但是在传出来的时候又被转型成了我们设置的T。 根据这一情况我们可知道,在泛型类或方法内,任何涉及到具体类型(即擦除后的类型的子类)的操作都不能进行比如: new T();或者T.play()(play为某子类的方法而不是擦除后的类的方法),又或者同时有T.method(T t),Tmethod(K t)的方法

  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,只是我也不确定是什么类。


以上部分转载自https://www.cnblogs.com/taojinxuan/p/7222287.html

以下是我的个人见解:首先解决作者提出的问题:为什么List<Number>不是List<Integer>的父类?

答:因为Java用的是单继承模型,他已经有一个爸爸了



然后,总结一下,这篇博客并没有很好的排版,所以阅读比较麻烦,为了让作者的意图更加明确地表达出来,我大概做一下整理。

我们可以通过<T extends ...>和<T super ...>的方式来设置上下限,也就是在编译的时候根据“里氏代换原则”进行设置默认的类型。如果没有设置上下限,就默认为Object。所谓的“擦除和转型都是在边界上发生的”就是这个意思。

然后作者说了get和add方法不能使用的问题,道理其实也是一样的,一切建立在“里氏代换原则”上,在设置泛型方法时,会设置一个默认值,也就是所谓的“边界”。add被禁止的情况下,因为子类有可能重写父类的某些方法(个人理解),所以如果要执行对应的T.method()时,就会出现使用的方法不对的问题,因为不能保证所有的方法在所有子类中的实现时相同的。

接着是get被禁用的情况,因为只设置了下限,也就是子类,这样如果在List中添加了父类,但是调用了一个子类所独有的方法,但是父类并没有这个方法,情况就会变得很尴尬。

基本就是这样,后面我将会继续探讨当泛型和lambda表达式结合的非常有趣的情况,从今天开始,我要好好写博客了==!

猜你喜欢

转载自blog.csdn.net/qq_31433709/article/details/80960094