摘要:泛型之上界下界通配符本教程是为编写的。这是在使用泛型编程时一个常见的误解,也是一个需要学习的重要概念。通配符使用指南学习使用泛型编程时,更令人困惑的一个方面是确定何时使用上限有界通配符以及何时使用下限有界通配符。
Java 泛型之上界下界通配符
本Java教程是为JDK 8编写的。本页描述的示例和实践没有利用后续版本中引入的改进。泛型,继承和子类
如你所知,只要类型兼容,就可以将一种类型的对象分配给另一种类型的对象。例如,你可以指定一个整数一个对象,因为对象是一个整数的超类型:
Object someObject = new Object(); Integer someInteger = new Integer(10); someObject = someInteger; // 好
在面向对象的术语中,这被称为“是一种”关系。由于Integer 是一种Object,因此允许赋值。但是Integer也是一种Number,所以下面的代码也是有效的:
public void someMethod(Number n){/ * ... * /} someMethod(new Integer(10)); // 好 someMethod(new Double(10.1)); // 好
泛型也是如此。您可以执行泛型类型调用,将Number作为其类型参数传递,如果参数与Number兼容,则允许任何后续的add调用:
Box
box.add(new Integer(10)); // 好
box.add(new Double(10.1)); // 好
现在考虑以下方法:
public void boxTest(Boxn){/ * ... * /}
它接受什么类型的论据?通过查看其签名,您可以看到它接受一个类型为Box
这是在使用泛型编程时一个常见的误解,也是一个需要学习的重要概念。
Box
注意:给定两个具体类型 A 和 B(例如,Number和Integer),MyClass 与 MyClass无关,无论 A 和 B 是否相关。MyClass 和 MyClass 的公共父是Object。
有关如何在类型参数相关时在两个泛型类之间创建类似子类型关系的信息,请参阅下面的通配符和子类型一节。
泛型类和子类型化您可以通过扩展(extends)泛型类或实现(implements)泛型接口来对其进行子类型化。一个类或接口的类型参数与另一个类或接口的类型参数之间的关系由extends和implements子句确定。
使用Collections类作为示例,ArrayList
Collection层次结构示例
显示Collection层次结构示例的图表:ArrayList是 List 的子类型,二者都是Collection 的子类型。
现在假设我们想要定义我们自己的列表接口PayloadList,它将可选值泛型类型参数P的与每个元素相关联。它的声明可能如下:
interface PayloadListextends List { void setPayload(int index, P val); ... }
PayloadList的以下参数化是List
PayloadListPayloadList PayloadList
PayloadList层次结构示例
PayLoadList层次结构的示意图:PayloadList通配符和子类型是List 的子类型,它是Collection 的子类型。 在PayloadList 的相同级别是PayloadList 和PayloadList 。
如 泛型,继承和子类一节中所述,泛型类之间或接口之间几乎并不因它们的类型参数而相关。但是,您可以使用通配符在泛型类或接口之间创建关系。
给定以下两个常规(非泛型)类:
class A { /* ... */ } class B extends A { /* ... */ }
编写以下代码是合理的:
B b = new B(); A a = b;
此示例显示常规类的继承遵循此子类型规则:如果B扩展A,则类B是类A的子类型。此规则不适用于泛型类型:
List lb = new ArrayList<>(); List la = lb; //编译时错误
鉴于Integer是Number的子类型,List
公共父类是List>
该图表显示 List和 List 的公共父级是未知类型的List.
尽管Integer是Number的子类型,但List
为了在这些类之间创建关系以便代码可以通过 List
List extends Integer> intList = new ArrayList<>(); List extends Number> numList = intList; // OK, List<?extends Integer>是 List< ? extends Number>的子类型
因为Integer是Number的子类型,而numList是Number对象的列表,所以intList(是一个Integer对象列表)和numList之间现在存在关系。下图显示了使用上限和下限通配符声明的多个 List 类之间的关系。
几个通用List类声明的层次结构。
图表显示: List通配符使用指南是List extends Integer>和 List<?super Integer>的子类型。 List extends Integer>是List extends Number> 的子类型,它是List >的子类型。 List 是List <?super Number> 和 List extends Number>的子类型。 List super Number>是List super Integer>的子类型, 且都是List>的子类型。
学习使用泛型编程时,更令人困惑的一个方面是确定何时使用上限有界通配符以及何时使用下限有界通配符。本文提供一些设计代码时要遵循的一些准则。
为讨论方便,认为变量具备两个功能:
一个“In”变量
“in”变量向代码提供数据。想象一下带有两个参数的复制方法:copy(src,dest)。该SRC参数提供的数据被复制,因此它是“in”参数。
一个“Out”变量
“out”变量保存数据以供其他地方使用。在复制示例中,copy(src,dest),dest参数接受数据,因此它是“out”参数。
当然,一些变量既用于“in”又用于“out”目的 - 这种情况也在本文中也用到了。
在决定是否使用通配符以及适合使用哪种类型的通配符时,可以使用“in”和“out”原则。以下列表提供了遵循的准则:
通配符指南:
使用extends关键字, 定义带有上界通配符的“in”变量。
使用super关键字,使用下界通配符定义“out”变量。
在可以使用Object类中定义的方法访问“in”变量的情况下,使用无界通配符。
在代码需要作为“in”和“out”变量访问变量的情况下,不要使用通配符。
这些指南不适用于方法的返回类型。应该避免使用通配符作为返回类型,因为它强制程序员使用代码来处理通配符。
List extends ...> 可以被非正式地认为是只读的,但这不是一个严格的保证。假设您有以下两个类:
class NaturalNumber { private int i; public NaturalNumber(int i) { this.i = i; } // ... } class EvenNumber extends NaturalNumber { public EvenNumber(int i) { super(i); } // ... }
请考虑以下代码:
Listle = new ArrayList<>(); List extends NaturalNumber> ln = le; ln.add(new NaturalNumber(35)); // compile-time error //编译时错误
因为List
您可以添加null。
你可以调用清除。
您可以获取迭代器并调用remove。
您可以捕获通配符并写入从列表中读取的元素。
你可以看到List extends NaturalNumber>在严格意义上不是只读的,但您可能会这样想,因为您无法存储新元素或更改列表中的现有元素。
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/77808.html
泛型通配符 在泛型代码中,称为通配符的问号(?)表示未知类型,通配符可用于各种情况:作为参数、字段或局部变量的类型,有时作为返回类型(尽管更好的编程实践是更加具体),通配符从不用作泛型方法调用、泛型类实例创建或超类型的类型参数。 以下部分更详细地讨论通配符,包括上界通配符、下界通配符和通配符捕获。 上界通配符 你可以使用上界通配符来放宽对变量的限制,例如,假设你要编写一个适用于List、List和...
泛型通配符使用指南 学习使用泛型编程时更困惑的一个方面是确定何时使用上界通配符以及何时使用下界通配符,此页面提供了设计代码时要遵循的一些准则。 对于本文的讨论,将变量看作提供的两个功能之一是有帮助的: 一个In变量 in变量向代码提供数据,想象一下带有两个参数的复制方法:copy(src, dest),src参数提供要复制的数据,因此它是in参数。 一个Out变量 out变量保存数据以供其他地方使...
摘要:虚拟机中并没有泛型类型对象,所有的对象都是普通类。其原因就是泛型的擦除。中数组是协变的,泛型是不可变的。在不指定泛型的情况下,泛型变量的类型为该方法中的几种类型的同一个父类的最小级,直到。 引入泛型的主要目标有以下几点: 类型安全 泛型的主要目标是提高 Java 程序的类型安全 编译时期就可以检查出因 Java 类型不正确导致的 ClassCastException 异常 符合越早出...
阅读 3095·2023-04-25 18:22
阅读 2293·2021-11-17 09:33
阅读 3242·2021-10-11 10:59
阅读 3223·2021-09-22 15:50
阅读 2782·2021-09-10 10:50
阅读 849·2019-08-30 15:53
阅读 425·2019-08-29 11:21
阅读 2669·2019-08-26 13:58