Java 泛型 通配符类型

@author ixenos

摘要:限定通配符类型、无限定通配符类型、与普通泛型区别、通配符捕获

通配符类型


  • 通配符的子类型限定(?都是儿孙)

    • <? extends T>
    • Pair<? extends Employee> managerrr = new Pair<Manager>(ceo,cfo); //Manager是Employee子类,这里协变了(泛型的通配符类型可协变,而一般的泛型不可协变)

    • 类型Pair<? extends Employee>的方法: //?是Manager的子类们
      • void setFirst(? extends Employee) //不可调用,编译器只知道?的取值范围是儿孙,不知道具体是啥类型(?拒绝传递任何特定的类型)
      • ? extends Employee getFirst()   //可调用,返回值是可协变的,将任意Employee子类型的返回值传递给Employee引用就是协变(体现了多态性)
  • 通配符的超类型限定(?都是祖宗)
    • <? super T>
    • 类型Pair<? super Manager>的方法: //?是Manager的父类们

      • void setFirst(? super Manager) //可调用,编译器不知道具体形参是,不能调用Employee对象,因为它不一定是爸爸,但可用任意Manager对象或其子类
      • ? super Manager getFirst()   //不可调用,返回类型是开放式的爸爸,可能类中修改了也不一定,不能保证类型安全,只能返回Object
  • 存取原则
    • 如果你想从一个数据类型里获取数据,使用 ? extends 通配符
    • 如果你想把对象写入一个数据结构里,使用 ? super 通配符
    • 如果你既想存,又想取,那就别用通配符。
    • get Extends, set Super

无限定通配符 <?>


以下引自:http://www.linuxidc.com/Linux/2013-10/90928p4.htm

无界通配符

知道了通配符的上界和下界,其实也等同于知道了无界通配符,不加任何修饰即可,单独一个“?”。如List<?>,“?”可以代表任意类型,“任意”也就是未知类型。

无界通配符通常会用在下面两种情况:

1、当方法是使用原始的Object类型作为参数时,如下:

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

可以选择改为如下实现:

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

这样就可以兼容更多的输出而不单纯是List<Object>,如下:

List<Integer> li = Arrays.asList(1, 2, 3); List<String> ls = Arrays.asList("one", "two", "three"); printList(li); printList(ls);

2、在定义的方法体的业务逻辑与泛型类型无关,如List.size,List.cleat。实际上,最常用的就是Class<?>,因为Class<T>并没有依赖于T。

最后提醒一下的就是,List<Object>与List<?>并不等同,List<Object>是List<?>的子类,<?>等同于<? extends Object>。还有不能往List<?> list里添加任意对象,除了null。

泛型方法与类型通配符的区别


泛型方法是确定泛型类型模板,允许类型形参被用来表示方法的一个或多个参数之间的类型依赖关系,或者方法返回值与参数之间的类型依赖关系,如果没有这样的类型依赖关系,就不应该使用泛型方法

类型通配符是不确定类型的模板,但确定泛型是<?>


removeAll(Collection<?> c)传入的形参可以是Collection<String>,也可以是其他,而换成E,就被限定了

原因是ArrayList<E>是个模板类,使用的时候总要实例化,比如实例化为ArrayList<String> list

那么这个removeAll形参也被实例化成Collection<E>,这样是违背了设计的初衷

通配符捕获


编写一个交换一个Pair元素的方法:public static void swap(Pair<?> p)

通配符不是类型变量因此不能在代码中使用“?” 作为一种类型:? t = p.getFirst(); // ERROR

但是我们交换的时候必须临时保存第一个元素,方法中要有一个泛型变量的引用

  金蝉脱壳,写一个辅助的泛型方法swapHelper:  

public static <T> void swapHelper(Pair<T> p){
T t = p.getFirst();
p.getFirst(p.setSecond());
p.setSecond(t);
}

  注意,swapHelper是一个泛型方法,而swap不是!swap具有固定的Pair<?>类型的参数

  现在由swap调用swapHelper:public static void swap(Pair<?> p) { swapHelper(p); } ,此时swapHelper方法的参数T捕获通配符,他不知道是哪种类型,但是这是一个明确的类型

  当编译器确信通配符表达的是单个、确定的类型时,通配符才能被当作静态类型被泛型捕获

    ArrayList<Pair<T>>中的T不能捕获ArrayList<Pair<?>>中的通配符,因为ArrayList可以保存两个Pair<?>,分别针对“?”的不同类型

Java 泛型 通配符类型的更多相关文章

  1. Java泛型:类型擦除

    类型擦除 代码片段一 Class c1 = new ArrayList<Integer>().getClass(); Class c2 = new ArrayList<String& ...

  2. Java泛型之类型擦除

    类型擦除 学过C++模板的,在使用Java泛型的时候,会感觉到有点不疑问,例如:(1)无法定义一个泛型数组.无法调用泛型参数对象中对应的方法(当然,通过extends关键字是可以做到,只是比较麻烦): ...

  3. java 泛型通配符 extends, super

    引自:http://sharewind.iteye.com/blog/1622164 关键字说明 ? 通配符类型 <? extends T> 表示类型的上界,表示参数化类型的可能是T 或是 ...

  4. Java泛型 通配符? extends与super

    Java 泛型 关键字说明 ? 通配符类型 <? extends T> 表示类型的上界,表示参数化类型的可能是T 或是 T的子类 <? super T> 表示类型下界(Java ...

  5. [转]JAVA泛型通配符T,E,K,V区别,T以及Class<T>,Class<?>的区别

    原文地址:https://www.jianshu.com/p/95f349258afb 1. 先解释下泛型概念 泛型是Java SE 1.5的新特性,泛型的本质是参数化类型,也就是说所操作的数据类型被 ...

  6. java泛型通配符?

    转自:http://www.linuxidc.com/Linux/2013-10/90928.htm T  有类型 ?  未知类型 一.通配符的上界 既然知道List<Cat>并不是Lis ...

  7. 理解Java泛型 通配符 ? 以及其使用

    什么是泛型: 泛型从字面上理解,是指一个类.接口或方法支持多种类型,使之广泛化.一般化和更加通用.Java中使用Object类来定义类型也 能实现泛型,但缺点是造成原类型信息的丢失,在使用中容易造成C ...

  8. JAVA 泛型 通配符? extends super限定,实例区分extends super限定的作用用法

    java泛型中的关键字 ? 表示通配符类型 <? extends T> 既然是extends,就是表示泛型参数类型的上界,说明参数的类型应该是T或者T的子类. <? super T& ...

  9. JAVA泛型通配符T,E,K,V区别,T以及Class<T>,Class<?>的区别

    1. 先解释下泛型概念 泛型是Java SE 1.5的新特性,泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数.这种参数类型可以用在类.接口和方法的创建中,分别称为泛型类.泛型接口.泛 ...

随机推荐

  1. js分页模板

    /** *参数说明: *currentPage:当前页数 *countPage:总页数 *changeMethod:执行java后台代码的js函数,即是改变分页数据的js函数 */ function  ...

  2. StringBuilder跟StringBuffer

    一直以来只知道StringBuffer是线程安全的,StringBuilder是线程不安全的, 所以通常情况下使用StringBuilder,这样可以提升效率!!! 今天由于想起StringBuild ...

  3. mysql连接失败或出现“Too many connections”错误

    mysql连接失败或出现"Too many connections"错误 # 按自己服务器的配置文件路径修改 vi /etc/my.cnf 查找:max_connections 修 ...

  4. git add -f

    git add -f 添加已被 .gitignore 忽略的文件/文件夹

  5. insertable = false, updatable = false的使用

    转自:insertable = false, updatable = false的使用 当使用JPA配置实体时,如果有两个属性(一个是一般属性,一个是多对一的属性)映射到数据库的同一列,就会报错. 这 ...

  6. Python学习笔记——基础篇【第五周】——正则表达式(re)

    目录 1.简介 2.字符匹配 1.简介:就其本质而言,正则表达式(或 RE)是一种小型的.高度专业化的编程语言,(在Python中)它内嵌在Python中,并通过 re 模块实现.正则表达式模式被编译 ...

  7. spring @Scheduled 执行2次

    今天遇到定时任务Scheduled 执行2次的情况,做一个简单的记录. 网上有好多办法,我几乎都试了一遍,我的情况下面的办法可用. 1. autodeploy属性值设置为false,如果此项设为tru ...

  8. 从P1到P7——我在淘宝这7年(转)

    作者: 赵超  发布时间: 2012-02-25 14:47  阅读: 114607 次  推荐: 153   [收藏] (一) 2011-12-08 [原文链接] 今天有同事恭喜我,我才知道自己在淘 ...

  9. BT 的相关资料

    1.Android中bluetooth的架构 http://blog.csdn.net/u011960402/article/details/11035947 2.Android4.0中Bluetoo ...

  10. C#变量修饰符

    访问修饰符 关键字包括:internal,public,protected和private,用于设置变量的访问级别.  public  变量可以做为它所属的类型的一个字段,可以在任何地方访问它.  i ...