EffectiveJava(28)怎么利用有限制的通配符类型来提升API的灵活性
有时候,我们需要的灵活性要比不可变类型所能提供的更多.所以针对一些通用性的方法,通常用泛型代替固定的数据类型,特别是当你要将某个方法打包成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的灵活性的更多相关文章
- Item 28 利用有限制通配符来提升API的灵活性
<? extends E> 和<? super E>的使用 <? extends E>的使用 Item 26 这是堆栈的公有API: public class ...
- 第28条:利用有限制通配符来提升API的灵活性
参数化类型是不可变的.对两个不同类型T1和T2而言,List<T1>与List<T2>没有父子类型关系. 考虑: public class Stack<E> { p ...
- 第二十八条:利用有限制通配符来提升API的灵活性
如第二十五条所述,参数化类型是不可变的.类型Type1和Type2而言,不管Type1与Type2的关系,List<Type1>既不是List<Type2>的子类型,也不是也不 ...
- effective-java学习笔记---使用限定通配符来增加 API 的灵活性31
在你的 API 中使用通配符类型,虽然棘手,但使得 API 更加灵活. 如果编写一个将被广泛使用的类库,正确使用通配符类型应该被认为是强制性的. 记住基本规则: producer-extends, c ...
- EffectiveJava——请不要在代码中使用原生态类型
先看一个栗子,看看能不能找出来里面的错误: /** * 请不要在新代码中使用原生态类型 * @author weishiyao * */ public class Test { public stat ...
- Effective Java 第三版——31.使用限定通配符来增加API的灵活性
Tips <Effective Java, Third Edition>一书英文版已经出版,这本书的第二版想必很多人都读过,号称Java四大名著之一,不过第二版2009年出版,到现在已经将 ...
- Java 泛型 通配符类型
Java 泛型 通配符类型 @author ixenos 摘要:限定通配符类型.无限定通配符类型.与普通泛型区别.通配符捕获 通配符类型 通配符的子类型限定(?都是儿孙) <? extends ...
- 免费申请通配符类型SSL证书
折腾起因 最近做了个小网站wawoo.fun,一个做mac壁纸的小网站,网站还处在初级阶段,不能跟大神的比.网站发布后发现因为没有使用https,谷歌浏览器会在地址栏提示网站不安全.因此想提升下网站的 ...
- Java泛型中的类型参数和通配符类型
类型参数 泛型有三种实现方式,分别是泛型接口.泛型类.泛型方法,下面通过泛型方法来介绍什么是类型参数. 泛型方法声明方式:访问修饰符 <T,K,S...> 返回类型 方法名(方法参数){方 ...
随机推荐
- macos不能打开windows samba共享问题(转载)
转自:https://www.macx.cn/thread-2095377-1-1.html?mod=viewthread&tid=2095377&extra=page%253D1&a ...
- 用Logger来解释拦截
HZ 动态代理学了 不知道在工作中杂用哦 HE 现在一般不会直接用吧,一般都是用aspectJ这种完整aop的实现 STST 拦截方法调用 HZ 我见过把所有accessor方法放到切面的 还有tra ...
- Luogu P2146 软件包管理器(树链剖分+线段树)
题意 给定\(n\)个软件包,每个软件包都有一个依赖软件包,安装一个软件包必须安装他的依赖软件包,卸载一个软件包必须先卸载所有依赖于它的软件包.给定\(m\)此操作,每次一个操作\(install/u ...
- sql 常见错误
notFound = 1403L; .dupKey = -1L; openCloseErr = -2117L; cursorNotOpenErr = -1002L; .nullCursor = -14 ...
- 树形dp(poj 1947 Rebuilding Roads )
题意: 有n个点组成一棵树,问至少要删除多少条边才能获得一棵有p个结点的子树? 思路: 设dp[i][k]为以i为根,生成节点数为k的子树,所需剪掉的边数. dp[i][1] = total(i.so ...
- 【BZOJ 1084】 1084: [SCOI2005]最大子矩阵 (DP)
1084: [SCOI2005]最大子矩阵 Description 这里有一个n*m的矩阵,请你选出其中k个子矩阵,使得这个k个子矩阵分值之和最大.注意:选出的k个子矩阵不能相互重叠. Input 第 ...
- ACM的奇计淫巧_bitset优化
什么是bitset bitset 是STL库中的二进制容器,根据C++ reference 的说法,bitset可以看作bool数组,但优化了空间复杂度和时间复杂度,并且可以像整形一样按位与或. 使用 ...
- Rxjava与Retrofit的使用
韩梦飞沙 韩亚飞 313134555@qq.com yue31313 han_meng_fei_sha ---- -----
- BZOJ 1229 [USACO2008 Nov]toy 玩具(三分+贪心)
[题木链接] http://www.lydsy.com/JudgeOnline/problem.php?id=1229 [题目大意] 每天对玩具都有一定的需求ni,每天可以花f价值每条购买玩具, 当天 ...
- 【动态规划】CDOJ1271 Search gold
方格取数. 但由于题意说金币数<0就死了,就不能继续转移. #include<cstdio> #include<algorithm> #include<cstrin ...