有时候,我们需要的灵活性要比不可变类型所能提供的更多.所以针对一些通用性的方法,通常用泛型代替固定的数据类型,特别是当你要将某个方法打包成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. mongo connect BI 连接至Power BI

    第一步:安装mongodb服务 官网地址:https://www.mongodb.com/download-center?jmp=nav#community mongodb的安装请参考:http:// ...

  2. Python 实现腾讯新闻抓取

    原文地址:http://www.cnblogs.com/rails3/archive/2012/08/14/2636780.htm 思路: 1.抓取腾讯新闻列表页面: http://news.qq.c ...

  3. 客户端 localStorage, sessionStorage, cookie 的区别

    SessionStorage, LocalStorage, Cookie这三者都可以被用来在浏览器端存储数据,而且都是字符串类型的键值对! 区别在于前两者属于WebStorage,创建它们的目的便是存 ...

  4. 【最小割】【Dinic】Gym - 101128F - Landscaping

    http://blog.csdn.net/lxy767087094/article/details/68942422 #include<cstdio> #include<cstrin ...

  5. 基于socket的udp传输,socketserver模块,进程

    基于UDP的套接字 udp是无连接的,先启动哪一端都不会报错 socket.SOCK_DGRAM 数据报协议 udp不会发送空数据,什么都不输入直接发送也会有报头发过去 服务端 import sock ...

  6. 83.(01背包)CYD刷题

    3130 CYD刷题  时间限制: 1 s  空间限制: 128000 KB  题目等级 : 钻石 Diamond 题解 查看运行结果 题目描述 Description 下午,CYD要刷题了,已知CY ...

  7. (Mark=转)ehcache memcache redis

    Ehcache 在java项目广泛的使用.它是一个开源的.设计于提高在数据从RDBMS中取出来的高花费.高延迟采取的一种缓存方案.正因为Ehcache具有健壮性(基于java开发).被认证(具有apa ...

  8. 模式匹配之Kmp算法

    Kmp: 算法定义借鉴wikipedia: http://en.wikipedia.org/wiki/Knuth%E2%80%93Morris%E2%80%93Pratt_algorithm#KMP_ ...

  9. Nginx + Keeplived双主测试

    Author: JinDate: 20130613Title: Nginx + Keeplived 双主测试 前言:一年多前做过一次测试,时间久了忘记了,现在又重新做一次 一.环境1.基本信息和规划p ...

  10. Android Activtity Security(转)

    Android四大组件之一--Activity安全详解. 原帖地址:http://drops.wooyun.org/tips/3936 0x00 科普 Android每一个Application都是由 ...