有时候,我们需要的灵活性要比不可变类型所能提供的更多.所以针对一些通用性的方法,通常用泛型代替固定的数据类型,特别是当你要将某个方法打包成Jar的时候.

结合之前的例子,我们增加尝试用有限制的通配符类型来加大方法的灵活性

   public class Stack<E> {
public void pushAll(Iterable<? entends E> src){
for(E e:src){
//push为一个将元素加入到数组中的方法
push(e);
}
}
}

这个方法按顺序将一系列的元素全部放到堆栈中.

如果我们将? entends E 替换成 E ,虽然它能通过编译,但是如果一个Stack调用了push(Integer),控制台将会报错.

但是用我们写的这个方法,不仅Stack可以编译通过,没有通过初始的pushAll声明进行编译的客户端代码一样可以.

那如果我们想从堆栈中弹出元素,并将元素添加到指定集合中,该怎么做防止它报错呢?

我们应该这么做 —>>>

   public void popAll(Collection<? super E> dst){
//isEmpty是一个判断数组是否为空的方法
while(!isEmpty()){
//pop()移出元素方法
dst.add(pop());
}
}

为什么pushAll()我们用extends,下面的例子却用super呢?

这是因为pushAll()中,我们是加入元素,pop是移除元素.所以,为了获得最大限度的灵活性,要在表示生产者或者消费者的输入参数上使用通配符类型,如果参数化类型表示一个T生产者,就使用

 static <E> e reduce(List<E> list,Function<E> f,E initVal);
----->>>>
static <E> E reduce(List<? extends E> list,Function<E> f,E initVal);

如果有一个List 想通过Function对它进行简化,上面那个不能通过编译,下面的就可以

当然,通配符类型并不是万能的,他不能作为返回类型,而且它除了为用户提供额外的灵活性之外,他还会强制用户在客户端代码中使用通配符类型.

成熟的使用通配符类型对于类的用户应该是几乎无形的,.它们是方法能够接受他们应该接受的参数,拒绝那些应该拒绝的参数.如果类的用户必须考虑通配符类型,类的API或许就会出错.我们可以通过一个不怎么优雅的方式解决这个问题

——->>>>通过一个显式的类型参数来告诉他要使用哪种类型

如:

    Set<Interger> integers = ...;
Set<Double> doubles = ...;
Set<Number> numbers = union(integers,doubles); ---->>>
Set<Number> numbers = Union.<Number>union(integers,doubles);

下面,我们通过一个方法解读一下有限制的通配符的使用

   //comparable始终是消费者
public static <T extends Comparable<? super T>> T max(List<? extends T> list){
//Iterator i = list.iterator(); //list不是一个List<T> 所以他的iterator方法没有返回Iterator<T>
//他返回的是某个子类型的iterator
Iterator<? extends T> i = list.iterator();
T result = i.next();
while(i.hasNext()){
T t = i.next();
if(t.compareTo(result)>0){
result = t;
}
return result;
}
}

类型参数和通配符之间具有双重性,许多方法可以利用其中一个或者另一个进行声明.那么,它们有什么区别呢?

—->>>>

//无限制类型参数
public static <E> void swap(List<E> list,int i,int j);
//无限制通配符
public static void swap(List<?> list,int i,int j);
在公共API中,推荐使用第二种,因为它更简单.如果类型参数只在方法声明中出现一次,就可以用通配符取代他,取代类型取决于其本身类型.

如果使用第二种方法,还需要注意,编写一个私有的辅助方法来捕捉通配符类型,因为List<?>不能存放null之外的任何值
--->>>
public static void swap(List<?> list,int i,int j){
swapHelper(list,i,j);
}
//swapHelper知道list是什么类型
private static <E> void swapHelper(List<E> list,int i,int j){
list.set(i,list.set(j,list.get(i)));
}
    总结:在API中使用通配符类型比较需要技巧,也会使API灵活的多.如果编写的是将被广泛实用的类库,则一定要适当地利用通配符.记住基本的原则:producer-extends,consumer-super(PECS).当然,所有的comparable和comparator都是消费者

EffectiveJava(28)怎么利用有限制的通配符类型来提升API的灵活性的更多相关文章

  1. Item 28 利用有限制通配符来提升API的灵活性

    <? extends E> 和<? super E>的使用   <? extends E>的使用 Item 26 这是堆栈的公有API: public class ...

  2. 第28条:利用有限制通配符来提升API的灵活性

    参数化类型是不可变的.对两个不同类型T1和T2而言,List<T1>与List<T2>没有父子类型关系. 考虑: public class Stack<E> { p ...

  3. 第二十八条:利用有限制通配符来提升API的灵活性

    如第二十五条所述,参数化类型是不可变的.类型Type1和Type2而言,不管Type1与Type2的关系,List<Type1>既不是List<Type2>的子类型,也不是也不 ...

  4. effective-java学习笔记---使用限定通配符来增加 API 的灵活性31

    在你的 API 中使用通配符类型,虽然棘手,但使得 API 更加灵活. 如果编写一个将被广泛使用的类库,正确使用通配符类型应该被认为是强制性的. 记住基本规则: producer-extends, c ...

  5. EffectiveJava——请不要在代码中使用原生态类型

    先看一个栗子,看看能不能找出来里面的错误: /** * 请不要在新代码中使用原生态类型 * @author weishiyao * */ public class Test { public stat ...

  6. Effective Java 第三版——31.使用限定通配符来增加API的灵活性

    Tips <Effective Java, Third Edition>一书英文版已经出版,这本书的第二版想必很多人都读过,号称Java四大名著之一,不过第二版2009年出版,到现在已经将 ...

  7. Java 泛型 通配符类型

    Java 泛型 通配符类型 @author ixenos 摘要:限定通配符类型.无限定通配符类型.与普通泛型区别.通配符捕获 通配符类型 通配符的子类型限定(?都是儿孙) <? extends ...

  8. 免费申请通配符类型SSL证书

    折腾起因 最近做了个小网站wawoo.fun,一个做mac壁纸的小网站,网站还处在初级阶段,不能跟大神的比.网站发布后发现因为没有使用https,谷歌浏览器会在地址栏提示网站不安全.因此想提升下网站的 ...

  9. Java泛型中的类型参数和通配符类型

    类型参数 泛型有三种实现方式,分别是泛型接口.泛型类.泛型方法,下面通过泛型方法来介绍什么是类型参数. 泛型方法声明方式:访问修饰符 <T,K,S...> 返回类型 方法名(方法参数){方 ...

随机推荐

  1. cordova plugin 大全及安装命令

    前言:用cordova开发hybrid app的过程中,由于是html5开发的app,在手机上很多权限受限制,这就导致了我们需要安装很多插件来弥补这一缺点.在网上搜了好久,感觉都不是很全,所以自己整理 ...

  2. (2)ansible主机清单文件inventory

    1)inventory作用 作用:通常用于定义要管理主机的认证信息,例如ssh登录用户名,密码等相关信息 缺省文件:/etc/ansible/hosts 2)定义主机组方式 #vim /etc/ans ...

  3. 如何使用 Java 对 List 中每个对象元素按时间顺序进行排序

    如何使用 Java 对 List 中每个对象元素按时间顺序进行排序 Java 实现 import java.text.SimpleDateFormat; import java.util.ArrayL ...

  4. Win7 + VirtualBox + CentOS (服务器版 无桌面) 使用共享文件夹

    http://jingyan.baidu.com/article/b2c186c8ffb607c46ff6ff61.html

  5. 使用0填充string(构造类似‘00001’的字符串)

    今天在对视频进行爬取的时候,发现url最后是000001,然后是000002,依次增加,而且每一个url请求只能得到一个分段了的视频,这种情况下构造url就成了一个问题. python有一个函数可以处 ...

  6. VMware Workstation的三种网络连接方式

    桥接模式(Bridged).NAT模式(地址转换模式).仅主机模式(Host-Only) 桥接模式就是将主机网卡与虚拟机的网卡利用虚拟网桥进行通信.在桥接的作用下,类似于把物理主机虚拟为一个交换机,所 ...

  7. Hive 空指针(NPE)异常

    空指针NullPointerException 1 Hive之前的一些BUG [HIVE-9430] - NullPointerException on ALTER TABLE ADD PARTITI ...

  8. 【map】【分解质因数】CDOJ1572 Espec1al Triple

    先把公比为1,即前项 中项 末项相同的统计出来.对每一类数C(n,3)即可. 然后我们发现,因为a1*a3=(a2)^2,所以a1和a3进行质因子分解之后,每一个质因子的指数的奇偶性必然相同,否则无法 ...

  9. css排版之-标准文档流

    标准流指的是在不使用其他的与排列和定位相关的特殊CSS规则时,各种元素的排列规则.HTML文档中的元素可以分为两大类:行内元素和块级元素.       1.行内元素不占据单独的空间,依附于块级元素,行 ...

  10. SSL 认证之后,request.getScheme()获取不到https的问题记录

    通过浏览器输入https://www.xxx.com,request.getScheme()获取到的确实http而不是https通过request.getRequestURL()拿到的也是http:/ ...