你是人间的四月天
第五章 泛型
- 二三 请不要在新代码中使用原生态类型
- 泛型信息可以在运行时被擦除
- 二四. 消除非受验警告
- 二五. 列表优先于数组
- 二六. 优先考虑泛型
- 二七. 优先考虑泛型方法
- 二八. 利用有限制通配符来提升API的灵活性
- PECS规则: producer-extends,consumer-super (PECS)
- 所有的comparable 和 comparator 都是消费者
二九. 优先考虑类型安全的异构容器
第六章 枚举和注解
三十. 用enum代替 int 常量
三一. 用实例域代替序数
三二. 用EnumSet代替位域(不看,用到了再说)
三三. 用EnumMap代替序数索引(同上)
三四. 用接口模拟可伸缩的枚举
- 虽然无法编写可扩展的枚举类型,却可以通过编写接口以及实现该接口的基础枚举类型,对它进行模拟。
三五. 注解优先于命名模式
三六. 坚持使用Override注解
三七. 用标记接口定义类型
第七章 方法
三八. 检查参数的有效性
三九. 必要时进行保护性拷贝
四十. 谨慎设计方法签名
- 谨慎的选择方法的名称
- 不要过于追求提供便利的方法
- 避免过长的参数列表。 目标是四个参数,或者更少。
- 谨慎的选择方法的名称
四一. 慎用重载(overload)
- 要调用哪个重载(overloading)方法是在编译时做出决定的
public class CollectionClassifier{ public static String classify(Set<?> s){ return "Set"; } public static String classify(List<?> lst){ return "List"; } public static String classify(Collection<?> c){ return "Unknow Collection"; } public static void main(String args){ Collection<?>[] collections = { new HashSet<String>(), new ArrayList<BigInteger>(), new HashMap<String,String>().values() }; for (Collection<?> c : collections){ System.out.println(classify(c)); } } }
对于上述代码,我们期望打印出来的结果为
Set,List,Unknow Collection
,然而结果却并不如意。实际打印出来的是三个Unknow Collection
也就是说对于重载方法(overload)是在编译时刻决定的,对于被覆写的方法(override),是在运行时刻决定的
以下是一个现在仍存在的问题(主要由ArrayList集合类的remove方法重载导致)
public class SetList {
public static void main(String[] args) {
Set<Integer> set = new TreeSet<Integer>();
List<Integer> list = new ArrayList<Integer>();
for (int i = -3; i < 3; i++) {
set.add(i);
list.add(i);
}
for (int i = 0; i < 3; i++) {
set.remove(i);
list.remove(i);
}
System.out.println(set+" "+ list);
}
}
输出结果:
[-3, -2, -1] [-2, 0, 2]
- Set的输出结果如同我们想的一样,但是List的结果不一样。实际发生的情况是:set.remove(E),选择重载方法的参数实际上是Integer,这里进行了自动装箱,把int装箱成了Integer;对于List,有两个重载函数,这里直接重载了list.remove(i),并没有重载到list.remove(E),是从list的指定位置进行remove,所以先移除了第0个,也就是-3,list中所有元素前移;再移除第1个,也就是list中当前第2个,也就是-1;以此类推,最后得到-2,0,2。我们可以在源码中看到:
来源: http://blog.csdn.net/a921122/article/details/54834858
这里List方法调用的是 remove(int )而不是 remove(Object)查看两者的源码:
remove(int )
public E remove(int index) { rangeCheck(index); modCount++; E oldValue = elementData(index); int numMoved = size - index - 1; if (numMoved > 0) System.arraycopy(elementData, index+1, elementData, index, numMoved); elementData[--size] = null; // clear to let GC do its work return oldValue; }
在每次移除index位置上的元素后,会将list前移。 过程为,第一次移除0号元素
-3
,返回新的list-2,-1,0,1,2
;第二次移除1号元素-1
,返回-2,0,1,2
依次往下进行,最后得到上述出乎意料的答案。
改进方法:list.remove(Integer)i);
list.remove(Integer.valueOf(i))
即可得到正确返回值。