看《Effective Java》学会的泛型设计
来源:互联网 发布:淘宝店开多久才有生意 编辑:程序博客网 时间:2024/04/29 19:04
一 先来小菜一碟
看代码:
public static <E> void christmas(List<E> list) { E[] snapshot = list.toArray(); for (E e : snapshot) { // } }
上面这段代码是编译不通过的, 出现了error:
Error:(56, 36) java: 不兼容的类型: java.lang.Object[]无法转换为E[]
也可以:
E[] snapshot = (E[])list.toArray();
加上这样的强制转换,compiler 在compile时会提示warning: unchecked cast: 'java.lang.Object[]' to 'E[]'
表明这里是类型不安全的,这样的转换是不好的。
根据《Effective Java》中所叙述的,数组和泛型有着非常不同的类型规则,数组是协变(covariant)和具体化的(reified),泛型是不可变(invariant)且可以被擦除的(erased)。数组提供了运行时类型的安全,但是没有编译时的类型安全。数组和泛型不能很好的混合使用,如果发现将它们混合起来使用,并且得到了编译时错误或警告,就应该是用List去代替Array。
上面的代码就可以修改为:
public static <E> void christmas(List<E> list) { List<E> snapshot = new ArrayList<>(list); for (E e : snapshot) { // } }
二 上主菜
内容都是来自《Effective Java》,记录以备后续学习。
下面的代码模拟 数据结构 栈(Stack)。
Stack 版本一
import java.util.Arrays;import java.util.EmptyStackException;public class StackDemo { private Object[] elements; private int size = 0; private static final int DEFAULT_INITIAL_CAPACITY = 16; public StackDemo() { elements = new Object[DEFAULT_INITIAL_CAPACITY]; } public void push(Object e) { ensureCapacity(); elements[size++] = e; } public Object pop() { if (size == 0) throw new EmptyStackException(); Object result = elements[--size]; elements[size] = null; // Eliminate obsolete reference return result; } public boolean isEmpty() { return size == 0; } private void ensureCapacity() { if (elements.length == size) elements = Arrays.copyOf(elements, 2 * size + 1); }}
为了让Stack泛型化并支持更多的类型,将StackDemo类中的Object替换成 形式类型参数 E,如下:
Stack版本二
import java.util.Arrays;import java.util.EmptyStackException;public class StackGericDemo<E> { private E[] elements; private int size = 0; private static final int DEFAULT_INITIAL_CAPACITY = 16; public StackGericDemo() { elements = new E[DEFAULT_INITIAL_CAPACITY]; } public void push(E e) { ensureCapacity(); elements[size++] = e; } public E pop() { if (size == 0) throw new EmptyStackException(); E result = elements[--size]; elements[size] = null; // Eliminate obsolete reference return result; } public boolean isEmpty() { return size == 0; } private void ensureCapacity() { if (elements.length == size) elements = Arrays.copyOf(elements, 2 * size + 1); }}
然而 elements = new E[DEFAULT_INITIAL_CAPACITY];
这行代码是有问题的,不能这样去创建数组,compiler报error错误。compiler表示不能证明你的程序是类型安全的。
为了抑制掉error错误, 可以加上泛型转换
elements = (E[]) new Object[DEFAULT_INITIAL_CAPACITY];
这样compiler就只会报unchecked cast的warning了,谢天谢地,但问题还没解决。
由于E是一个不可具体化的(non-reifiable), 编译器不能确定你的程序是类型安全的,但是写代码的你可以证明啊。elements这个变量保存在一个private字段中,没有提供对应的client可以访问到elements的方法,保存到elements中的值是通过push方法加进去的,是E类型的,so compiler爆出的unchecked cast warning对这个Stack的程序是没有危害的(no harm)。
一旦你证实到这样的unchecked cast是安全的,在小局部范围使用@SuppressWarnings("unchecked")
将这个警告给抑制掉吧(作为一个有代码洁癖的你)。
新版本的Stack Java Code:
import java.util.Arrays;import java.util.EmptyStackException;public class StackGericDemo<E> { private E[] elements; private int size = 0; private static final int DEFAULT_INITIAL_CAPACITY = 16; // The elements array will contain only E instances from push(E). // This is sufficient to ensure type safety, but the runtime // type of the array won't be E[]; it will always be Object[]! @SuppressWarnings("unchecked") public StackGericDemo() { /** * you can’t create an array of a non-reifiable type, such as E * 不能创建不可具体化的数组 */// elements = new E[DEFAULT_INITIAL_CAPACITY]; /** * 这是类型不安全的 * 但这里是能保证这个程序是类型安全的,因为指定了类型参数 E * 这里的 unchecked cast can do no harm */ elements = (E[]) new Object[DEFAULT_INITIAL_CAPACITY]; } public void push(E e) { ensureCapacity(); elements[size++] = e; } public E pop() { if (size == 0) throw new EmptyStackException(); E result = elements[--size]; elements[size] = null; // Eliminate obsolete reference return result; } public boolean isEmpty() { return size == 0; } private void ensureCapacity() { if (elements.length == size) elements = Arrays.copyOf(elements, 2 * size + 1); }}
Stack版本三
还有一种方法是将E[]
换成Object[]
, code :
import java.util.Arrays;import java.util.EmptyStackException;public class StackGericPerfectDemo<E> { private Object[] elements; private int size = 0; private static final int DEFAULT_INITIAL_CAPACITY = 16; public StackGericPerfectDemo() { elements = new Object[DEFAULT_INITIAL_CAPACITY]; } public void push(E e) { ensureCapacity(); elements[size++] = e; } public E pop() { if (size == 0) throw new EmptyStackException(); /** * unchecked cast, 是否能转换 * Stack.java:19: warning: [unchecked] unchecked cast found : Object, required: E E result = (E) elements[--size]; Because E is a non-reifiable type, there’s no way the compiler can check the cast at runtime. */ // push requires elements to be of type E, so cast is correct @SuppressWarnings("unchecked") E result = (E) elements[--size]; elements[size] = null; // Eliminate obsolete reference return result; } public boolean isEmpty() { return size == 0; } private void ensureCapacity() { if (elements.length == size) elements = Arrays.copyOf(elements, 2 * size + 1); }}
版本二和版本三相比,禁止an uncheck cast to an array type 比禁止a scalar type(标量类型)要更加危险一些,所以建议还是版本三的方案。但是Stack的实际的使用中,会在代码的多个地方从数组中读取值,选择版本三的方案的话会需要many cast to E
,版本二只需要a single cast to E[]
,由此版本二的Stack的泛型设计会更常用一点。
- 看《Effective Java》学会的泛型设计
- 看《Effective Java》学会的泛型设计--上下限
- Effective Java: 方法的设计
- 结合effective c++看MFC的类设计
- Effective Java:类和接口的设计
- 学会看外面的风景
- 学会看openstack的日志
- 学会用理性的眼光看周围
- 看名言后的心得体会学会融会贯通
- 看了一则广告,学会for循环~【java】
- Effective Java 学习笔记之泛型
- Effective Java设计原则 感想--equals
- 【Effective Java】架构与设计模式
- 学会看预算
- 学会看开机信息
- 学会看开机信息
- 学会看源码
- 学会Settings必看
- 【hdu 4135】Co-prime
- Android Support Library介绍
- Netty 学习 - 异步操作中的Future和Promise
- windows 7下NDK 编译libcurl
- MacOS下的内网穿透映射工具(wlt)
- 看《Effective Java》学会的泛型设计
- Android四大组件—ContentProvider
- 小谈 Java 泛型
- C控制语句:循环
- Cesium学习笔记(5)
- shell查找指定目录下的关键字文件
- Struts知识点概况(一)
- 设置apk权限allow
- python property 研究