Java泛型扩展及超级存在着什么区别

  介绍

小编给大家分享一下Java泛型扩展及超级存在着什么区别,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获、下面让我们一起去了解一下吧!

& lt;扩展T>和& lt; ?超级T>是Java泛型中的“通配符(通配符)”和“边界(范围)”的概念。

    <李> & lt; ? T>延伸:是指“上界通配符(上界通配符)”李 <李> & lt; ?超级T>:是指“下界通配符(下界通配符)”

<强>为什么要用通配符和边界?

使用泛型的过程中,经常出现一种很别扭的情况,比如按照题主的例子,我们有水果类,和它的派生类苹果类。

类水果{}
类苹果延伸水果{}

然后有一个最简单的容器:板类。盘子里可以放一个泛型的“东西”。我们可以对这个东西做最简单的“放”和“取”的动作:设置()和()方法。

类Plate 

现在我定义一个“水果盘子”,逻辑上水果盘子当然可以装苹果:

<代码> Platepfruit=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>覆盖下图中蓝色的区域。

癑ava泛型扩展及超级存在着什么区别"

<强>什么是下界?

相对应的,“下界通配符(下界通配符)”:

<代码> Plate<?超级Fruit>

表达的就是相反的概念:一个能放水果以及一切是水果基类的盘子.Plate<?超级Fruit>是Plate的基类,但不是Plate的基类。对应刚才那个例子,Plate<?超级Fruit>覆盖下图中红色的区域。

癑ava泛型扩展及超级存在着什么区别"

<强>上下界通配符的副作用

边界让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泛型扩展及超级存在着什么区别