谈谈自己对java泛型 T、K、V、E、?的理解

开始的时候只明白泛型是限定类型,泛型的优点就是不需要coder再强制转换类型,而是在编译的时候检查类型,类型转换也变成了自动的和隐式的。但是对于 T、K、V、E、?这样的符号还不是理解的很深刻,今天研读了一篇文章,恍然大悟,遂记录下。

先大致解释下 T、K、V、E、?几个字符意思(约定俗成的意思):

  • T:表示一个具体的java类型,type的首字母。
  • K,V:一般指代键值对,即key,value
  • E:一般指代元素,即element
  • :表示不确定的一个java类型,与T有不同之处。也被称为通配符或者无界通配符。

T的用法:

谈谈自己对java泛型 T、K、V、E、?的理解_第1张图片

最常见的就是定义类的时候定义泛型,此时该类下的方法可以定义泛型T,这里方法的 T 和类定义的 T 一定是一样的类型,保证了类型的一致性。这里简单描述下通配符 ? 和 T 的区别,正如我注释中所述,通配符 ? 常常用来作为一个声明出现,而 T 作为一个确定的类型出现,这个确定的意思是,出现在这里的 T 为同一类型,通配符是无法保证的,即 T 会指向同一个将来被传进来的类型,通配符可能指向多个。

谈谈自己对java泛型 T、K、V、E、?的理解_第2张图片

用 T 限定:这里的list必须都是同一类型。

谈谈自己对java泛型 T、K、V、E、?的理解_第3张图片

用通配符 ?来声明,则类型可以不一致,如上图所示。

还有一个细节:谈谈自己对java泛型 T、K、V、E、?的理解_第4张图片

此处做了一个对比, 如果当前类使用了泛型定义,即 class A ,并且方法里需要使用的泛型与类定义的泛型一致,那么可以不用单独在方法前声明,如果类未使用泛型定义,或者当前方法的泛型类型与类所定义的不一致,那么需要在方法之前声明泛型类型,如上test方法所示。而 通配符 ?由于本身的不确定性,也就没有办法声明。

?的用法

称为通配符,或者无界通配符,相对应的还有有界通配符,又分为上界通配符、下界通配符、多重限定。

一般在不知具体类型时,可以声明为通配符,如果确定类型可以用 T。

谈谈自己对java泛型 T、K、V、E、?的理解_第5张图片

上界通配符

建立animal类,dog类继承animal。

  • 在方法内部使用参数的时候:如果是上界通配符限定,即表示小于等于的范围,约定上限(该泛型的父类一定是animal)。当然此处也可以用 T ,因为T 可以追溯到上限的animal类,不会丢失类信息,可以用animal来接收元素信息;但是插入不适用,由于插入时无法获知是哪个子类,导致安全性降低有可能导致类型转换错误,编译器一概拒绝这样的插入,所以set方法可以说在这种情况下是被禁止的。
  • 在方法外部调用方法的时候,只要传入的参数是继承自 animal 或本身即可。

谈谈自己对java泛型 T、K、V、E、?的理解_第6张图片

下界通配符

相对应的还有下界通配符:

谈谈自己对java泛型 T、K、V、E、?的理解_第7张图片

相对于上界通配符,下界通配符限定的是该泛型一定是其父类或本身(在我的例子中,该泛型的类型一定是animal的父类或本身)。

  • 方法内部使用参数的时候,只能进行 set 操作,因为只要是animal 类即可插入,由于里氏替换原则,子类可以出现在父类出现的地方,故 animal子类也可以插入。如果要取值,由于它被传入的是 animal 的父类,在内部又被插入 animal子类,故类型信息无法推断,所以取值必定丢失类型信息,由此可见,下限通配符限定的参数只用来赋值合适,取值不合适。
  • 方法外部调用传参时:只能传 animal 的父类或其本身。

上界通配符和下界通配符描述的是这个泛型的类的限定关系:如果是上界,那么证明该泛型的父类必然是animal;如果是下界,该泛型必定是animal的父类,当然都包含等于的情况。个人理解描述的是父子类的范围。

多重边界:

如果是有多个边界,需要用 & 符号连接。

谈谈自己对java泛型 T、K、V、E、?的理解_第8张图片

先说明需要满足的条件,如果是接口,那么 在 extends关键字后的顺序无所谓。如果是有类的话,那么需要注意顺序,需要放在接口的前面。通配符是无法多重限定的。

这样编译是会报错的:

谈谈自己对java泛型 T、K、V、E、?的理解_第9张图片

调整好顺序:

谈谈自己对java泛型 T、K、V、E、?的理解_第10张图片

关于多重边界,看了资料,写了这两个例子后(求数字最大值为参考资料练习的例子),有所领悟:

首先说下这多重边界的好处。也许不完全对,纯属个人理解。谈谈我个人写的例子(第二个):T 限定边界,为 A 和 B 的共有子类型,我理解的是,继承实现关系即实现了共有。此时这个 T 类型参数便获得了 A 和 B 限定的方法和属性。这样做的有利之处在于可以作为一个通用的方法。

用我学习的例子来说明(上面求最大数):integer作为number的子类,并且它也实现了comparable接口,那么传进来的integer是可以直接享用number类和comparable接口所提供的方法和属性。那么只要是符合这个条件的参数传进来,这里的方法都是可用的。

以上大概就是我个人学习之后的新理解,仅作记录。

你可能感兴趣的:(java)