资讯专栏INFORMATION COLUMN

Java™ 教程(泛型通配符)

qingshanli1988 / 766人阅读

泛型通配符

在泛型代码中,称为通配符的问号()表示未知类型,通配符可用于各种情况:作为参数、字段或局部变量的类型,有时作为返回类型(尽管更好的编程实践是更加具体),通配符从不用作泛型方法调用、泛型类实例创建或超类型的类型参数。

以下部分更详细地讨论通配符,包括上界通配符、下界通配符和通配符捕获。

上界通配符

你可以使用上界通配符来放宽对变量的限制,例如,假设你要编写一个适用于ListListList的方法,你可以通过使用上界通配符来实现这一点。

要声明一个上界通配符,请使用通配符("?"),后跟extends关键字,后跟上界,请注意,在此上下文中,extends在一般意义上用于表示“extends”(如在类中)或“implements”(如在接口中)。

要编写适用于NumberNumber的子类型列表的方法,例如IntegerDoubleFloat,你可以指定List<?extends Number>List一词比List更具限制性,因为前者只匹配Number类型的列表,而后者匹配Number类型或其任何子类的列表。

考虑以下process方法:

public static void process(List list) { /* ... */ }

上界通配符,其中Foo是任何类型,匹配FooFoo的任何子类型,process方法可以像Foo类型一样访问列表元素:

public static void process(List list) {
    for (Foo elem : list) {
        // ...
    }
}

foreach子句中,elem变量遍历列表中的每个元素,现在可以在elem上使用Foo类中定义的任何方法。

sumOfList方法返回列表中数字的总和:

public static double sumOfList(List list) {
    double s = 0.0;
    for (Number n : list)
        s += n.doubleValue();
    return s;
}

以下代码使用Integer对象列表打印sum = 6.0

List li = Arrays.asList(1, 2, 3);
System.out.println("sum = " + sumOfList(li));

Double值列表可以使用相同的sumOfList方法,以下代码打印sum = 7.0

List ld = Arrays.asList(1.2, 2.3, 3.5);
System.out.println("sum = " + sumOfList(ld));
无界通配符

使用通配符(?)指定无界通配符类型,例如List,这称为未知类型的列表,有两种情况,无界通配符是一种有用的方法:

如果你正在编写可以使用Object类中提供的功能实现的方法。

当代码使用泛型类中不依赖于类型参数的方法时,例如,List.sizeList.clear,事实上,经常使用Class,因为Class中的大多数方法都不依赖于T

考虑以下方法,printList

public static void printList(List list) {
    for (Object elem : list)
        System.out.println(elem + " ");
    System.out.println();
}

printList的目标是打印任何类型的列表,但它无法实现该目标 — 它只打印一个Object实例列表,它不能打印ListListList等,因为它们不是List的子类型,要编写通用的printList方法,请使用List

public static void printList(List list) {
    for (Object elem: list)
        System.out.print(elem + " ");
    System.out.println();
}

因为对于任何具体类型AListList的子类型,你可以使用printList打印任何类型的列表:

List li = Arrays.asList(1, 2, 3);
List  ls = Arrays.asList("one", "two", "three");
printList(li);
printList(ls);
在本课程的示例中使用了Arrays.asList方法,此静态工厂方法转换指定的数组并返回固定大小的列表。

重要的是要注意ListList是不一样的,你可以将ObjectObject的任何子类型插入List,但是你只能在List中插入null,通配符使用指南部分提供了有关如何确定在给定情况下应使用哪种通配符(如果有)的更多信息。

下界通配符

上界通配符部分显示上界通配符将未知类型限制为该类型的特定类型或子类型,并使用extends关键字表示,以类似的方式,下界通配符将未知类型限制为该类型的特定类型或超类型。

使用通配符(?)表示下界通配符,后跟super关键字,后跟下界:

你可以指定通配符的上界,也可以指定下界限,但不能同时指定两者。

假设你要编写一个将Integer对象放入列表的方法,为了最大限度地提高灵活性,你希望该方法可以处理ListListList — 任何可以保存Integer值的方法。

要编写适用于IntegerInteger超类型列表的方法,例如IntegerNumberObject,你可以指定ListList一词比List更具限制性,因为前者仅匹配Integer类型的列表,而后者匹配任何类型为Integer的超类型的列表。

以下代码将数字1到10添加到列表的末尾:

public static void addNumbers(List list) {
    for (int i = 1; i <= 10; i++) {
        list.add(i);
    }
}
通配符和子类型

如泛型、继承和子类型中所述,泛型类或接口不相关,仅仅因为他们的类型之间存在关系,但是,你可以使用通配符在泛型类或接口之间创建关系。

给定以下两个常规(非泛型)类:

class A { /* ... */ }
class B extends A { /* ... */ }

编写以下代码是合理的:

B b = new B();
A a = b;

此示例显示常规类的继承遵循此子类型规则:如果B扩展A,则B类是A类的子类型,此规则不适用于泛型类型:

List lb = new ArrayList<>();
List la = lb;   // compile-time error

假设IntegerNumber的子类型,ListList之间的关系是什么?

虽然IntegerNumber的子类型,但List不是List的子类型,事实上,这两种类型不相关,ListList的公共父级是List

为了在这些类之间创建关系以便代码可以通过List的元素访问Number的方法,请使用上界的通配符:

List intList = new ArrayList<>();
List  numList = intList;  // OK. List is a subtype of List

因为IntegerNumber的子类型,而numListNumber对象的列表,所以intListInteger对象列表)和numList之间现在存在关系,下图显示了使用上界和下界通配符声明的多个List类之间的关系。

上一篇:类型推断 下一篇:泛型通配符捕获和Helper方法

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/72943.html

相关文章

  • Java教程(目录)

    Java™ 教程 Java教程是为JDK 8编写的,本页面中描述的示例和实践没有利用在后续版本中引入的改进。 Java教程是希望使用Java编程语言创建应用程序的程序员的实用指南,其中包括数百个完整的工作示例和数十个课程,相关课程组被组织成教程。 覆盖基础知识的路径 这些教程以书籍的形式提供,如Java教程,第六版,前往Amazon.com购买。 入门 介绍Java技术和安装Java开发软件并使用...

    lifesimple 评论0 收藏0
  • Java教程泛型配符捕获和Helper方法)

    泛型通配符捕获和Helper方法 在某些情况下,编译器会推断出通配符的类型,例如,列表可以定义为List,但是在评估表达式时,编译器会从代码中推断出特定类型,此场景称为通配符捕获。 在大多数情况下,你不必担心通配符捕获,除非你看到包含短语capture of的错误消息。 WildcardError示例在编译时产生捕获错误: import java.util.List; public class ...

    ChristmasBoy 评论0 收藏0
  • Java教程泛型配符使用指南)

    泛型通配符使用指南 学习使用泛型编程时更困惑的一个方面是确定何时使用上界通配符以及何时使用下界通配符,此页面提供了设计代码时要遵循的一些准则。 对于本文的讨论,将变量看作提供的两个功能之一是有帮助的: 一个In变量 in变量向代码提供数据,想象一下带有两个参数的复制方法:copy(src, dest),src参数提供要复制的数据,因此它是in参数。 一个Out变量 out变量保存数据以供其他地方使...

    raledong 评论0 收藏0
  • Java教程泛型的限制)

    泛型的限制 要有效地使用Java泛型,必须考虑以下限制: 无法使用基元类型实例化泛型类型 无法创建类型参数的实例 无法声明类型为类型参数的静态字段 无法对参数化类型使用强制类型转换或instanceof 无法创建参数化类型的数组 无法创建、捕获或抛出参数化类型的对象 无法重载将每个重载的形式参数类型擦除为相同原始类型的方法 无法使用基元类型实例化泛型类型 考虑以下参数化类型: class P...

    Bowman_han 评论0 收藏0
  • Java教程泛型、继承和子类型)

    泛型、继承和子类型 如你所知,只要类型兼容,就可以将一种类型的对象分配给另一种类型的对象,例如,你可以将Integer分配给Object,因为Object是Integer的超类型之一: Object someObject = new Object(); Integer someInteger = new Integer(10); someObject = someInteger; // OK ...

    AaronYuan 评论0 收藏0

发表评论

0条评论

qingshanli1988

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<