Java5.0泛型编程 --from Smth

来源:互联网 发布:如何提高英语写作 知乎 编辑:程序博客网 时间:2024/05/16 10:26

Generic Programming in Java
0. 基本概念
Java的泛型语法类似于C++,但实质完全不同。
对于List<A> 和 List<B>,C++将产生两份不同的代码。而java只有一份代码。 Java不会做
任何的代码扩展,Java对泛型的实现是通过Object和cast来模拟的。There aren’t multip
le copies of the code: not in source, not in binary, not on disk and not in memo
ry.
这里有一个重要的概念:erasure
erasure决定了generic的java程序编译后在旧版的java程序和jvm看来是什么样子。前面说
到Java不会把Collection<T>这样的东西根据T不同编译出多份代码来。那么在jvm看起来,
就应该所有的Collection<T>都一样。erasure做的就是这个工作。erasure将抹掉任何模版
的信息:Collection<T>这样东西,erasure后会变成Collection,T变成Object…… javac会
尽最大的努力来留下尽可能多的信息:比如:指定某个T一定要extends Comparable,那么e
rasure后将会把T换成Comparable而非Object。

BTW:这也不同于.net 2.0,在.net 2.0中,虽然只产生一份字节码,但是.net vm在运行时
会扩展出多份native code。

1. 使用系统的模版
1.1 Container
        List Set ...
1.2 Holder
        WeakReference ThreadLocal ...
1.3 强检查的容器
java在编译的时候检查模版是否匹配,编译后产生的代码等同于旧代码。所以在不需要cas
t的时候(比如put),你可以随便放点什么进去。比如:
                List<String> ls = new ArrayList<String>();
                List l = ls;
                l.add(1); // line3
                String p = ls.get(0);
在add的时候不会出错。而在get的时候才出一个exception。这样查错的时候很麻烦。
可以用以下的语句来产生强检查的代码,以方便查错。
        List<String> ls = Collections.checkedList(new ArrayList<String>(), Strin
g.class);
        这回使得 line 3 产生一个 ClassCastException.

2.  定义自己的模版
2.1 基本型态
public interface List<E> {
        void add(E x);
        Iterator<E> iterator();
}

public interface Iterator<E> {
        E next();
        boolean hasNext();
}
2.2 继承
List<String> ls = new ArrayList<String>(); //1
List<Object> lo = ls; //2
line 1 是合法的继承。
line 2 会被编译器干掉,为了sun省心。这点和array不一样,很让人不爽。
test.java:7: incompatible types
found   : java.util.List<java.lang.String>
required: java.util.List<java.lang.Object>
2.3 通配符
因为2.2 line 2 的继承行不通,所以这样的代码就很没用了:
public void drawAll(List<Shape> shapes) {
        for (Shape s: shapes) { s.draw(this); }
}
因为List<Circle>不能传给drawAll,这给我们带来了很大困扰。为此Sun提供了bounded wi
ldcard功能:
public void drawAll(List<? extends Shape> shapes) {
        for (Shape s: shapes) { s.draw(this); }
}
在这个例子里List<Circle>就可以传进来了。
BTW: List<?> == List<? extends Object>!=List

但是不爽的是,我们仍然不能在drawAll里面把Shape的某个子类对象传给shapes。如下的代
码是非法的:
public void addRectangle(List<? extends Shape> shapes) {
        shapes.add(0, new Rectangle()); // compile-time error!
}
除了extends以外,我们也可以用super来限定通配符:
class TreeSet<E> {
        TreeSet(Comparator<? super E> c)
}
这样TreeSet<String>就可以接受Comparator<Object>作为参数,这是合理的:-D
2.4 模版方法:
这是一个jdk自己的例子:
public static <E> Set<E> checkedSet(Set<E> s, Class<E> type);
static <T> void fromArrayToCollection(T[] a, Collection<T> c)
{
        for (T o : a) { c.add(o); }
}

注意:这里Class<E>是为能够拿到一个Class对象,因为我们在函数里没法写E.class这样的
语法。
        public static <T> void doSomeThing(T obj)
        {
                System.out.println(T.class);    // cannot select from a type var
iable
        }
BTW:因为该死的继承机制,Class<E>只能接受E.class作为参数。
关于模版的实例化。参考这个:
Object[] oa = new Object[100];
Collection<Object> co = new ArrayList<Object>();
fromArrayToCollection(oa, co);// T inferred to be Object
String[] sa = new String[100];
Collection<String> cs = new ArrayList<String>();
fromArrayToCollection(sa, cs);// T inferred to be String
fromArrayToCollection(sa, co);// T inferred to be Object
Integer[] ia = new Integer[100];
Float[] fa = new Float[100];
Number[] na = new Number[100];
Collection<Number> cn = new ArrayList<Number>();
fromArrayToCollection(ia, cn);// T inferred to be Number
fromArrayToCollection(fa, cn);// T inferred to be Number
fromArrayToCollection(na, cn);// T inferred to be Number
fromArrayToCollection(na, co);// T inferred to be Object
fromArrayToCollection(na, cs);// compile-time error

另外:这样的写法也是可以的
public <T extends E> boolean addAll(Collection<T> c);
更复杂的写法:
// T继承自Comparable<T>
public static <T extends Comparable<T>> max(Collection<T> coll)
// T继承自Comparable<T的某个基类>
public static <T extends Comparable<? super T>> max(Collection<T> coll)
// T继承自Comparable<T的某个基类>以及Object。
public static <T extends Object & Comparable<? super T>> T max(Collection<T> col
l)
上面这句话的作用主要是为了使得max的erasure返回类型是Object和Comparable中的第一个
(Object),以便和旧的class文件保持二进制兼容。
如果我们不写Object,那么max在编译以后会变成:
public static Comparable max(Collection coll)
而在老版本里面,max是这样的:
public static Object max(Collection coll)
这样就使得所用用了老版本max的java程序都需要重新编译,这是一个噩梦。而<T extends
Object & Comparable<? super T>>很好的解决了这个问题。