小编给大家分享一下Java泛型扩展及超级存在着什么区别,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获、下面让我们一起去了解一下吧!
& lt;扩展T>和& lt; ?超级T>是Java泛型中的“通配符(通配符)”和“边界(范围)”的概念。
- <李> & lt; ? T>延伸:是指“上界通配符(上界通配符)”李> <李> & lt; ?超级T>:是指“下界通配符(下界通配符)”李>
<强>为什么要用通配符和边界? 强>
使用泛型的过程中,经常出现一种很别扭的情况,比如按照题主的例子,我们有水果类,和它的派生类苹果类。
类水果{}
引用>
类苹果延伸水果{}然后有一个最简单的容器:板类。盘子里可以放一个泛型的“东西”。我们可以对这个东西做最简单的“放”和“取”的动作:设置()和()方法。
类Plate现在我定义一个“水果盘子”,逻辑上水果盘子当然可以装苹果:
<代码> Plate
pfruit=new Plate (新的苹果());代码> 但实际上Java编译器不允许这个操作。会报的错,“装苹果的盘子”无法转换成“装水果的盘子”。
<代码>错误:不兼容的类型:Plate
不能转化为Plate 代码> 所以问题就来了。实际上,编译器脑袋里认定的逻辑是这样的:
<李>苹果是一种水果李> <李>装苹果的盘子少好是一个装水果的盘子李>
所以,就算容器里装的东西之间有继承关系,但容器之间是没有继承关系的,所以我们不可以把板的引用传递给盘子。
为了让泛型用起来更舒服,太阳的大脑袋们就想出了& lt;及# 63;扩展T>和& lt;及# 63;超级T>的办法,来让“水果盘子”和“苹果盘子”之间发生关系。
<强>什么是上界? 强>
下面代码就是“上界通配符(上界通配符)”:
<代码> Plate<?扩展Fruit> 代码>
翻译一下就是:一个能放水果以及一切是水果派生类的盘子。再直白点就是:啥水果都能放的盘子。这和我们人类的逻辑就比较接近了.Plate<? Fruit>延伸和Plate
最大的区别就是:Plate<?扩展Fruit>是Plate 以及Plate 的基类。直接的好处就是,我们可以用“苹果盘子”给“水果盘子”赋值了。 <代码> Plate<及# 63;Fruit>延伸;p=新Plate
(新的苹果());代码> 如果把水果和苹果的例子再扩展一下,食物分成水果和肉类,水果有苹果和香蕉,肉类有猪肉和牛的肉,苹果还有两种青苹果和红苹果。
//Lev 1 类食物{}//Lev 2 类水果延伸食品{} 类肉延伸食品{}//Lev 3 苹果类扩展了水果{} 类香蕉水果延伸{} 扩展了猪肉类{} 扩展肉牛肉类{}//Lev 4 类RedApple扩展苹果{} 类GreenApple扩展苹果{}在这个体系中,下界通配符Plate<?扩展Fruit>覆盖下图中蓝色的区域。
<强>什么是下界? 强>
相对应的,“下界通配符(下界通配符)”:
<代码> Plate<?超级Fruit> 代码>
表达的就是相反的概念:一个能放水果以及一切是水果基类的盘子.Plate<?超级Fruit>是Plate
的基类,但不是Plate 的基类。对应刚才那个例子,Plate<?超级Fruit>覆盖下图中红色的区域。
<强>上下界通配符的副作用强>
边界让Java不同泛型之间的转换更容易了。但不要忘记,这样的转换也有一定的副作用,那就是容器的部分功能可能失效。
还是以刚才的板为例。我们可以对盘子做两件事,往盘子里设置()新东西,以及从盘子里得到()东西。
类Plate上界& lt;及# 63;扩展T>不能往里存,只能往外取
& lt;及# 63;扩展Fruit>会使往盘子里放东西的设置()方法失效。但取东西得到()方法还有效。比如下面例子里两个组()方法,插入苹果和水果都报错:
Plate<及# 63;Fruit>延伸;pfruit=new Plate(新的苹果());//不能存入任何元素 pfruit。集(新水果());//错误 pfruit。集(新苹果());//错误//读取出来的东西只能存放在水果或它的基类里。 水果newFruit1=pfruit.get (); 对象newFruit2=pfruit.get (); 苹果newFruit3=pfruit.get ();//错误 Java泛型扩展及超级存在着什么区别