`

Java泛型5---通配符类型

    博客分类:
  • JAVA
 
阅读更多
固定的泛型类型系统使用起来并没有那么令人愉快。Java的设计者发明了一种巧妙(仍然是安全的)“解决方案”:通配符类型。
例如:Pair<? extends B>,表示任何泛型Pair类型,它的类型参数是B的子类,如Pair<BSub>,但不是Pair<Date>。
构造一个方法:
    public static void executeFun(Pair<BSub> p){
        p.getFirst().fun();
        p.getSecond().fun();
    }
不能将Pair<B>传给这个方法,方法的功能受到了很大的限制。解决方法是:使用通配符类型。
    public static void executeFun(Pair<? extends B> p){
        ...
    }
类型Pair<BSub>是Pair<? extends B>的子类型。
使用通配符会通过Pair<? extends B>的引用破坏Pair<BSub>吗?答案是不能。
Pair<BSub> bsp = new Pair<BSub>();
Pair<? extends B> bp = bsp;//ok
bp.setFirst(new B());//Error
对setFirst的调用有一个类型错误。找知道其中缘由,请仔细看看类型Pair<? extends B>。它的方法如下所示:
? extends B getFirst();
void setFirst(? extends B);
这样不可能调用setFirst方法。编译器只知道它需要某个B类型的字类型,但不知道具体是什么类型。它拒绝传递任何特定的类型---毕竟,?不能用来匹配。
使用getFirst就不存在这个问题:将getFirst的返回值赋给一个B的引用是完全合法的。
这就是引入有限定的通配符的关键之处。现在已经有办法区分安全的访问器方法和不安全的更改器方法了。
 
通配符的超类型限定
通配符限定与类型变量限定十分相似。但是,它还有一个附加的能力,即可以指定一个超类型限定,如下所示:
? super BSub
这个通配符限制为BSub的所有超类型。已有的super关键字十分准确的描述了这种关系。
为什么要这样做?带有超类型限定的通配符的行为与前面介绍的相反。可以向方法提供参数,但不能使用返回值。例如,Pair<? super BSub>有方法
void setFirst(? super BSub)
? super BSub getFirst()
编译器不知道setFirst方法的确切类型,但是可以用任意BSub对象(或子类型)调用它,而不能用B对象调用。然而,如果调用getFirst,返回的对象类型不会得到保证,只能把它赋给一个Object,如果要将返回值赋给一个非Object的变量要使用强制的类型转换。直观地讲,带有超类型限定的通配符可以向泛型对象写入,带有子类型限定的通配符可以从泛型对象读取。
 
下面是超类型限定的另一种应用。Comparable接口本身就是一个泛型类型。它被声明如下:
public interface Comparable<T> {
    public int compareTo(T o);
}
在此,类型变量指示了o参数的类型。例如,String类实现Comparable<String>,它的compareTo方法被声明为
public int compareTo(String o)
很好,显式的参数有一个正确的类型。在JDK1.5之前,o是一个Object,并且该方法的实现需要强制的类型转换。因为Comparable是一个泛型类型,也许可以把ArrayAlg类的min方法做得更好一些?可以这样声明:
    public static <T extends Comparable<T>> T min(T[] a){
        if(a == null || a.length == 0){
            return null;
        }
        T t = a[0];
        for(int i=1;i<a.length;i++){
            if(t.compareTo(a[i]) > 0){
                t = a[i];
            }
        }
        return t;
    }
看起来,这样写只适用T extends Comparable更彻底,并且对于许多类来讲会工作得更好。例如,如果计算一个String数组的最小值,T就是String类型的,而String是Comparable<String>的字类型。但是,当处理一个GregorianCalendar对象的数组时,就会出现问题。GregorianCalendar是Calendar的子类,并且Calendar实现了Comparable<Calendar>。因此GregorianCalendar实现的是Comparable<Calendar>,而不是Comparable<GregorianCalendar>。
在这种情况下,超类型可以用来进行救助:
    public static <T extends Comparable<? super T>> T min(T[] a){ ... }
现在compareTo(? super T)
有可能它被声明为使用类型T的对象,也有可能使用T的超类型(例如,当T是GregorianCalendar)。无论如何,传递一个T类型的对象给compareTo方法都是安全的。
对于初学者来说,<T extends Comparable<? super T>>这样的声明看起来有点吓人。但很遗憾,因为这一声明的意图在于帮助应用程序员排除调用参数上的不必要的限制。对泛型没有兴趣的应用程序员可能很快就学会掩盖这些声明,想当然地认为库程序员做的都是正确的。如果是一名库程序员,一定要习惯于通配符,否则,就会受到用户的责备,还要在代码中随意地添加强制类型转换直至代码可以编译。
 
无限定通配符
还可以使用无限定的通配符,例如,Pair<?>。咋看起来好像和原始的Pair类型一样。实际上,有很大的不同。类型Pair<?>有方法如:
? getFirst()
void setFirst(?)
getFirst的返回值只能赋给一个Object。setFirst方法不能被调用,甚至不能用Object调用。Pair<?>和Pair的本质不同在于:可以用任意的Object对象调用原始的Pair类的setFirst()方法。
为什么要使用这样脆弱的类型?它对于许多简单的操作非常有用。例如,下面这个方法将用来测试一个pair是否包含了指定的对象,它不需要实际的类型。
    public static boolean hasNulls(Pair<?> p){
        return p.getFirst() == null || p.getSecond() == null;
    }
如果把它转化成泛型方法,可以避免使用通配符类型:
    public static<T> boolean hasNulls(Pair<T> p)
但是,带有通配符类型的版本可读性更强。
分享到:
评论

相关推荐

    java泛型源码-Java-Generics-Tutorial:教程的源代码

    java泛型源码Java泛型用法 步骤1 原始类型有问题。 第2步 使用泛型类型。 第三步 车库和车辆。 原始类型。 第四步 首先尝试生成车库。 木星在我的车库里。 第5步 泛型上限。 第6步 TripleGarage 步骤7 试图使用泛型...

    Java 泛型通配符的一个实例

    * 一个参数通配符的实例 * 说明:对一个包含了数值元素的集合进行汇总运算。在这种情况下,用户并不关心 * 集合中的每一个对象是什么类型,只要它是数值型即可,而且,用户也希望集合中可以 * 存放不同类型的数值...

    java基础-泛型通配符

    java基础-泛型通配符

    Java泛型通配符

    NULL 博文链接:https://mydownload.iteye.com/blog/1330570

    java泛型例子

    java泛型例子 内涵泛型类,泛型接口,泛型方法,泛型通配符使用,泛型上界下界,泛型数组,嵌套泛型等,很详细。放入myeclipse可用,已测试。

    Generic_3(泛型限定(下限)-泛型限定(上限的体现)-泛型限定(下限的体现)-泛型限定(通配符的体现)-集合查阅的技巧)

    泛型是Java SE 1.5的新特性,泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。这种参数类型可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口、泛型方法。 Java语言引入泛型的好处是...

    Java 泛型最全指南(定义和使用+继承泛型类/实现泛型接口+泛型的边界+通配符+类型擦除)

    Java 泛型最全指南(定义和使用+继承泛型类/实现泛型接口+泛型的边界+通配符+类型擦除)

    JAVA泛型总结

    java 泛型详解 实例 class Point class Notepad,V&gt;{ // 此处指定了...通配符、受限泛型、泛型无法向上转型、泛型接口、泛型方法、通过泛型方法返回泛型类型实例、使用泛型统一传入的参数类型、泛型数组、泛型的嵌套设置

    Java泛型:概念、用法与优势

    Java泛型是一种强大的特性,它使得我们可以编写更加通用、类型安全的代码。通过使用泛型类、泛型方法以及通配符,我们可以处理多种数据类型而无需重复编写代码,并且在编译时进行类型检查,避免了运行时的类型错误。...

    Java语言 泛型讲解案例代码 (泛型类、泛型接口、泛型方法、无界及上下限通配符、泛型对协变和逆变的支持、类型擦除 ...)

    学习和理解Java泛型的基本概念和语法; 实际项目中需要使用泛型来增加类型安全性和重用性的开发任务。 目标: 本代码资源的目标是帮助读者理解泛型的用法和优势,并通过实际的示例代码加深对泛型的掌握。读者可以...

    详谈Java泛型中T和问号(通配符)的区别

    下面小编就为大家带来一篇详谈Java泛型中T和问号(通配符)的区别。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧

    Java 基础泛型.pdf

    Java泛型的核心思想是类型参数化,即在类、接口或方法的定义中使用类型参数来代替具体的类型,这些类型参数在实例化时被具体类型替换,从而实现了通用性和类型安全。 主要特性包括 1. 类型参数:在类、接口或方法的...

    Java中的泛型

    讲述泛型及泛型的子类型、通配符

    java泛型常用通配符实例解析

    主要介绍了java泛型常用通配符实例解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下

    java泛型总结.docx

    泛型类型:定义泛型类型时,使用尖括号,并在尖括号中指定类型参数。例如,List表示一个字符串类型的列表。 泛型方法:定义泛型方法时,在方法名后面使用尖括号,并在尖括号中指定类型参数。例如,public static &lt;T&gt;...

    java深度历险

    JAVA泛型 28 类型擦除 28 实例分析 29 通配符与上下界 30 类型系统 31 开发自己的泛型类 32 最佳实践 32 参考资料 33 目录 3 JAVA注解 34 使用注解 34 开发注解 35 处理注解 35 实例分析 38 参考资料 39 JAVA反射与...

    Java中泛型通配符的使用方法示例

    主要介绍了Java中泛型通配符的使用方法,结合实例形式分析了java中泛型通配符的功能、语法及在泛型类创建泛型对象中的使用方法,需要的朋友可以参考下

    Java泛型类型通配符和C#对比分析

    下面小编就为大家带来一篇Java泛型类型通配符和C#对比分析。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧

    一看就懂 详解JAVA泛型通配符T,E,K,V区别

    泛型从字面上理解,是指一个类、接口或方法支持多种类型,使之广泛化、一般化和更加通用。通配符只有在修饰一个变量时会用到,使用它可方便地引用包含了多种类型的泛型;下面我们来深入了解一下吧

    Java基础篇:泛型.pdf

    该文档主要包括为什么使用泛型、在集合中使用泛型、自定义泛型结构、泛型在集成上的体现以及通配符的使用等内容

Global site tag (gtag.js) - Google Analytics