Generics Types 泛型学习笔记<二>
作者:冰云
时间:2004-02-24
联系:icecloud(AT)sina.com
Blog:http://icecloud.51.net
首先我要特别感谢Schlemiel,他指出了类型协变的问题。其实我以前也看到过gigix翻译的那篇文,但是没看懂也没经大脑。经Schlemiel一提醒,我才意识到,这是泛型的规范。再翻出来看,如醍醐灌顶,真良师益友也。
我写文章的目的,就是为了能够找到更多同好进行交流。没有交流,没有氛围,绝对不会有提高。谢谢Schlemiel,每次读他的文章或评论,都能受到很大的启发。
如果需要深入了解泛型,请阅读程序员杂志2003第7期,或
http://gigix.cool2u.net/download/Variance_in_Java.pdf
ok,继续上次的学习笔记。这篇22页的文章正好读了3天。每天从地铁东四十条到西直门,每次读7页。如果哪位晚上6:30-7:00间看到地铁上有个人拿着本电脑书在读,不妨上来问问是不是俺 ^___^||
另:后面文中每章出现了不少重点,我就按照和文中章节一样的标题。Notes则自己编号。
这次特地把字体变为14px,不知道各位看起来如何啊?会不会太大?
5 泛型方法(函数)
根据上一条,既然通配符类型是只读的,那么怎样才能在函数中写入呢?这就引入了泛型函数(Generic Mehtods):
<T> static void fromArrayToCollection(T[] a, Collection<T> c) {
for (T o : a){
c.add(o); //correct
}
}
Collection<Object> co = new ArrayList<Object>;
Collection<String> cs = new ArrayList<String>;
fromArrayToCollection(new Integer[100], co); //correct
fromArrayToCollection(new Number[100], cs); // error
可以知道,不需要传递世纪参数到泛型方法,编译器会自行推断类型。
那么,什么时候使用泛型方法,什么时候使用通配符类型呢?可以这样理解:
Note 5: 可以采用泛型方法来保证读写操作的类型安全。泛型方法相当于原来的多态方法,它的效果是允许执行时选用不同的参数类型,而通配符方法相当于接受Object的方法。
原文:This tells us that the type argument is being used for polymorphism; its only effect is to allow a variety of actual argument types to be used at different invocation sites.
考虑下面的方法,
public static <T, S extends T> void copy (
List <T> dest, List <S> src) ();
public static <T> void copy (
List <T> dest, List <? extends T> src) ();
哪一个更清楚明了一点?因为S只用了一次,因此可以用通配符类型取代S。这样更简练。通配符类型的另一个好处是,他们可以用在方法外面,作为值域(fields)、本地变量(local variables)或数组的类型。如
static List<List<? extends Shape>> history
= new ArrayList<List<? extends Shape>>();
public void drawAll(List<? extends Shape> shapes){
history.addLast(shapes);
}
说实话,我看到上面的变量定义简直想骂人。这个是定义了一个List的List,也就是history是保存List<? extends Shape>的List。
6 泛型与旧代码
在泛型代码中使用旧代码可能导致类型安全隐患。
public interface Inventory {
void setCats(Collection c);
Collection getCats();
}
Collection<Cat> c = new ArrayList<Cat>();
c.add(new BlackCat());
Inventory.setCats(c);
Collection<Cat> k = Inventory.getCats();
Note 6: 一般编译器无法知道Collection引用了何种类型,这样的没有带有类型的Collection称为row type。这样的类型表示一些未知类型,相当于Collection<?>。
原文:The compiler has no way of knowing what kind of collection the type Collection refers to. It’s called a raw type. It’s more accurate to say that the type Collection denotes a collection of some unknown type like Collection<?>.
这种情况下,把Collection<?>赋值给Collection<Cat>是合法的,但是会出现一个unchecked warning,未检查警告。表示编译器无法保证它的正确性。因此使用旧代码时候请注意unchecked warning.
Note 7: 泛型编译时会被编译器进行一次称为erasure的过程。可以认为就是将泛型的代码转换为非泛型版本。最终的结果是:JVM不会检查类型安全和完整性,就算出现了unchecked warning也一样。Erasure会删除所有的泛型信息,如将List<String>转换为List,所有的类型变量被替换为最上限的,如Object。并且不管结果代码如何,都会增加类型转换。
原文:Generics are implemented by the Java compilers as a front-end conversion called ensure. It just like a source-to-source translation. As a result, the type safety and integrity of the JVM are never at risk, even in presence of unchecked warnings. Erasure throws out all type information between angle brackets. For example, List<String> is converted into List. All remaining uses of type variables are replaced by the upper bound type variable (usually Object). And, whenever the resulting code isn’t type-correct, a cast to the appropriate type is inserted.
在旧代码中使用泛型代码。与上面情况类似,会出现unchecked warning.
7 Fine Print 美好蓝图?(搞不懂作者的意图)
泛型类被所有的它的子类共享。
List<String> l1 = new ArrayList<String>();
List<Integer> l2 = new ArrayList<Integer>();
assert(l1.getClass()==l2.getClass()); // return true
Note 8: 所有的泛型类拥有相同的runtime类(其实从上面一条可以知道),并且,静态变量和方法也会在类的实例间共享。这就是为何在静态方法、变量或初始化中引用类型参数是非法的。
原文:All instances of generics class have the same run-time class. As consequence, the static variables and methods of a class are also shared among all the instances. That’s why it is illegal to refer to the type parameters of a type declaration in a static method or initializer, or in the declaration or initializer of a static variable.
另一个隐含的事实,是无法判断一个类是否instanceof 其泛型类。或类型转换一个对象到一个泛型类型。
Collection<String> cs = new ArrayList<String>;
if (cs instanceof Collection<String>) {} //非法
(Collection<String>) cs; // unchecked warning
<T>T badCast(T t, Object o) {return (T)o};// unchecked warning
Note 9: 类型变量在运行时不存在!这意味着泛型不会占用时间或空间的性能,但不幸的是,你不能可靠的使用类型转换。
原文:type variables don’t exist at run time. This means that they entail no performance overhead in either time nor space. It also means that you can’t reliably use them in casts.
数组可能无法成为一个参数类型。除非是一个通配符类型。
List<String> [] lsa = new List<String>[10]; // 不允许
= new List<?>[10]; // 允许
Object o = isa;
Object[] oa = (Object[])o;
oa[1] = new ArrayList<Integer>();
String s = lsa[1].get(); //允许的情况下,runtime error
可以声明类型变量的数组,但是不能初始化(new)建立一个新数组。如new T[100],因为类型变量运行时并不存在。
词汇表:
参数类型:parameterized type,形如:new ArrayList<?>,new ArrayList<String>
类型变量:type variable,形如:<T> T getCollection()…
通配符类型:wildcard type,形如:new ArrayList<?>
有界通配符类型:bounded wildcard type,形如:new ArrayList<? extends Object>
原生类型:raw type,指在泛型代码中使用Collection, ArrayList等旧形式
未检查警告:unchecked warning,Java1.5代码安全警告。
To be continue ...
版权声明:
本文由冰云完成,首发于CSDN,作者保留中文版权。
未经许可,不得使用于任何商业用途。
欢迎转载,但请保持文章及版权声明完整。
如需联络请发邮件:icecloud(AT)sina.com
本文地址:http://com.8s8s.com/it/it16820.htm