时间:2021-05-19
泛型,继承和子类
如你所知,只要类型兼容,就可以将一种类型的对象分配给另一种类型的对象。例如,你可以指定一个整数一个对象,因为对象是一个整数的超类型:
在面向对象的术语中,这被称为“是一种”关系。由于Integer 是一种Object,因此允许赋值。但是Integer也是一种Number,所以下面的代码也是有效的:
泛型也是如此。您可以执行泛型类型调用,将Number作为其类型参数传递,如果参数与Number兼容,则允许任何后续的add调用:
现在考虑以下方法:
它接受什么类型的论据?通过查看其签名,您可以看到它接受一个类型为Box<Number>的参数。但是,这是什么意思?您是否可以按照您的预期传递Box<Integer>或Box<Double>?答案是“否”,因为Box<Integer>和Box<Double>不是Box<Number>的子类型。
这是在使用泛型编程时一个常见的误解,也是一个需要学习的重要概念。
Box<Integer>不是Box<Number>的子类型,即使Integer是Number的子类型。
注意:给定两个具体类型 A 和 B(例如,Number和Integer),MyClass<A> 与 MyClass<B>无关,无论 A 和 B 是否相关。MyClass<A> 和 MyClass<B> 的公共父是Object。
有关如何在类型参数相关时在两个泛型类之间创建类似子类型关系的信息,请参阅下面的通配符和子类型一节。
泛型类和子类型化
您可以通过扩展(extends)泛型类或实现(implements)泛型接口来对其进行子类型化。一个类或接口的类型参数与另一个类或接口的类型参数之间的关系由extends和implements子句确定。
使用Collections类作为示例,ArrayList<E> 实现 List<E>,List<E> 扩展Collection<E>。所以 ArrayList<String>是List<String>的子类型,它是Collection<String>的子类型。只要不改变类型参数,就会在类型之间保留子类型关系。
显示Collection层次结构示例的图表:ArrayList<String>是 List<String>的子类型,二者都是Collection<String>的子类型。
现在假设我们想要定义我们自己的列表接口PayloadList,它将可选值泛型类型参数P的与每个元素相关联。它的声明可能如下:
PayloadList的以下参数化是List<String>的子类型:
PayLoadList层次结构的示意图:PayloadList<String,String>是List<String>的子类型,它是Collection<String>的子类型。 在PayloadList<String,String>的相同级别是PayloadList <String,Integer>和PayloadList<String,Exceptions>。
通配符和子类型
如 泛型,继承和子类一节中所述,泛型类之间或接口之间几乎并不因它们的类型参数而相关。但是,您可以使用通配符在泛型类或接口之间创建关系。
给定以下两个常规(非泛型)类:
编写以下代码是合理的:
此示例显示常规类的继承遵循此子类型规则:如果B扩展A,则类B是类A的子类型。此规则不适用于泛型类型:
鉴于Integer是Number的子类型,List<Integer> 和 List<Number> 之间的关系是什么?
尽管Integer是Number的子类型,但List<Integer>不是List<Number>的子类型,实际上,这两种类型不相关。List<Number>和 List<Integer> 的公共父是 List<?>。
上界(extends)的通配符与下界(super)通配符
为了在这些类之间创建关系以便代码可以通过 List<Integer> 的元素访问Number的方法,请使用上界的通配符:
因为Integer是Number的子类型,而numList是Number对象的列表,所以intList(是一个Integer对象列表)和numList之间现在存在关系。下图显示了使用上限和下限通配符声明的多个 List 类之间的关系。
通配符使用指南
学习使用泛型编程时,更令人困惑的一个方面是确定何时使用上限有界通配符以及何时使用下限有界通配符。本文提供一些设计代码时要遵循的一些准则。
为讨论方便,认为变量具备两个功能:
一个“In”变量
“in”变量向代码提供数据。想象一下带有两个参数的复制方法:copy(src,dest)。该SRC参数提供的数据被复制,因此它是“in”参数。
一个“Out”变量
“out”变量保存数据以供其他地方使用。在复制示例中,copy(src,dest),dest参数接受数据,因此它是“out”参数。
当然,一些变量既用于“in”又用于“out”目的 - 这种情况也在本文中也用到了。
在决定是否使用通配符以及适合使用哪种类型的通配符时,可以使用“in”和“out”原则。以下列表提供了遵循的准则:
通配符指南:
这些指南不适用于方法的返回类型。应该避免使用通配符作为返回类型,因为它强制程序员使用代码来处理通配符。
List<? extends ...> 可以被非正式地认为是只读的,但这不是一个严格的保证。假设您有以下两个类:
请考虑以下代码:
因为List<EvenNumber>是List<? extends NaturalNumber>,您可以赋值le给ln。但是你不能使用ln将自然数添加到偶数列表中。列表中的以下操作是可能的:
你可以看到List<? extends NaturalNumber>在严格意义上不是只读的,但您可能会这样想,因为您无法存储新元素或更改列表中的现有元素。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
声明:本页内容来源网络,仅供用户参考;我单位不保证亦不表示资料全面及准确无误,也不保证亦不表示这些资料为最新信息,如因任何原因,本网内容或者用户因倚赖本网内容造成任何损失或损害,我单位将不会负任何法律责任。如涉及版权问题,请提交至online#300.cn邮箱联系删除。
简介前两篇文章介绍了泛型的基本用法、类型擦除以及泛型数组。在泛型的使用中,还有个重要的东西叫通配符,本文介绍通配符的使用。这个系列的另外两篇文章:Java泛型总
c#的泛型没有类型通配符,原因是.net的泛型是CLR支持的泛型,而Java的JVM并不支持泛型,只是语法糖,在编译器编译的时候都转换成object类型类型通配
本文实例讲述了Java中泛型通配符的使用方法。分享给大家供大家参考,具体如下:一点睛引入通配符可以在泛型实例化时更加灵活地控制,也可以在方法中控制方法的参数。语
前言Kotlin泛型的基本语法类似于Java,不过出于型变安全,不支持Java中的,通配符型变约束,而是采用类似C#的in,out用于支持协变和逆变,这同时避免
java泛型的详解及实例Java在1.5版本中增加了泛型,在没有泛型之前,从集合中读取每一个对象都需要进行强转,如果一不小心插入了类型错误的对象,在运行时就会报