分类: Java
2009-11-27 16:02:18
接着上次笔记的讲。
根据上次笔记,java泛型已经介绍完了,但是事情还没有结束。
在很长一段时间内,并不是每个程序员都会使用最新版本的jdk,说不定他就在使用没有引入
泛型的jdk1.4。而且他在jdk1.4基础上已经开发了很多代码,难以一下子改成泛型的。
所以我们面临的新问题就是如何利用以前的代码。
还是像上次笔记一样,提出一个问题,解释一个问题。
在没有泛型的时候,某个程序员写了如下代码:
package com.Fooblibar.widgets;
public interface Part { ...}
public class Inventory {
/**
* Adds a new Assembly to the inventory database.
* The assembly is given the name name, and consists of a set
* parts specified by parts. All elements of the collection parts
* must support the Part interface.
**/
public static void addAssembly(String name, Collection parts) {...}
public static Assembly getAssembly(String name) {...}
}
public interface Assembly {
Collection getParts(); // Returns a collection of Parts
}
如果第一眼没看出什么,那么我稍微改点便于快速理解,反正估计没人有耐心来研究我的笔记。
package com.Fooblibar.widgets;
public interface 组件 { ... }
public class 仓库 {
public static void add一类组件(String 这类组件的名字, Collection parts) { ... }
public static void Assembly get一类组件(String name) { ... }
}
public interface Assembly {
Collectioin getParts();
}
为了便于介绍,我自己加了段这段代码的应用场景,一下子就知道了。这个场景也是在没有泛型的时候,程序员写的。
class 小刀 implements Part { ... }
class 菜刀 implements Part { ... }
public class Main {
public staic void main(String[] args) {
Collection c = new ArrayList();
c.add(new 小刀());
c.add(new 菜刀());
仓库.add一类组件("刀",c);
Collection k = 仓库.get一类组件(刀).getParts();
}
}
但是有个程序员用上了泛型的新特性,于是他这样写:
class 小刀 implements Part { ... }
class 菜刀 implements Part { ... }
public class Main {
public static void main(String[] args) }
Collection
c.add(new 小刀());
c.add(new 菜刀());
仓库.add一类组件("刀",c);
Collection
}
}
两段代码的差别只在于
好了,到现在为止我们已经模拟出了一个泛型代码来使用非泛型代码的例子了。
会出现一个问题。
就是Collection
具体发生在
仓库.add一类组件("刀",c); //这里c是 Collection
调用
public static void add一类组件(String 这类组件的名字, Collection parts) { ... }
的时候
那么这里就会出现未检查错误。相当于
Collection parts = new Collection
这里会出现危险操作,因为parts可能加入非Part实现类进入容器。
还有一个问题。
就是Collection
这句话相当于,
Collection
以上两个错误只是会引发潜在的代码危险。
当然,如果不允许这样赋值就一切解决了,只是java设计人员是故意这样保持前后兼容才这样搞的。
这句话我说了没用,英文原文:(没现英语水平,多谢配合)
So raw types are very much like wildcard types, but they are not typechecked as
stringently. This is a deliberate design decision, to allow generics to interoperate with
pre-existing legacy code.
我的注解:raw types就是 Collection ,wildcard就是Collection
ok,现在又讨论一个新问题
就是
List
List xs = ys;
xs.add(new Integer(0)); //编译会警告不会出错,但是运行时出现ClassCastException
到现在为止,应该可以理解这个。
编译只警告是因为想兼容,运行出错是因为类型不对。
这里有人会问
Collection parts = new Collection
这个为什么parts能add(new Object());运行时不出错
而
Collection
Collection parts2 = parts;
parts2.add(new Object());
会出错。
我也不知道~
但是我测试过确实是这样的。下面是两段测试代码,可以试试。
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
public class JavaGeneric62 {
public String loophole(Object x) {
Collection
Collection xs = ys;
xs.add(x);
return ys.iterator().next().toString();
}
public static void main(String[] args) {
new JavaGeneric62().loophole(new Object());
}
}
上面代码运行时出错,出错如下
Exception in thread "main" java.lang.ClassCastException: java.lang.Object cannot be cast to java.lang.String
at JavaGeneric62.loophole(JavaGeneric62.java:10)
at JavaGeneric62.main(JavaGeneric62.java:14)
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
public class JavaGeneric62 {
public String loophole(Object x) {
Collection ys = new LinkedList
ys.add(x);
return ys.iterator().next().toString();
}
public static void main(String[] args) {
new JavaGeneric62().loophole(new Object());
}
}
上面代码无警告运行时无异常
总结:java virtual machine是无敌的~
好了,上面讨论的是使用泛型的程序员在调用不使用泛型的代码的时候发生的故事,
那么下面讨论一下不使用泛型的程序员在调用泛型代码的时候发生什么故事:
看了上面的例子大家已经进入状态,那么直接贴出代码,而且其中故事已经非常分明了
package com.Fooblibar.widgets;
public interface Part { ...}
public class Inventory {
/**
* Adds a new Assembly to the inventory database.
* The assembly is given the name name, and consists of a set
* parts specified by parts. All elements of the collection parts
* must support the Part interface.
**/
public static void addAssembly(String name, Collection
public static Assembly getAssembly(String name) {...}
}
public interface Assembly {
Collection
}
package com.mycompany.inventory;
import com.Fooblibar.widgets.*;
public class Blade implements Part {
}
public class Guillotine implements Part {
}
public class Main {
public static void main(String[] args) {
Collection c = new ArrayList();
c.add(new Guillotine()) ;
c.add(new Blade());
Inventory.addAssembly(”thingee”, c); // 1: unchecked warning
Collection k = Inventory.getAssembly(”thingee”).getParts();
}
}
其中会有警告,原因跟上面一样。
那么下面开始新的问题
首先我们认识一下java.lang.Object这个对象的一个方法:
Class extends Object> getClass();
返回一个对象的运行时类。
看个我弄的例子和结果就知道,然后开始讨论新问题。
class ParentClass {
}
class ChildClass extends ParentClass {
}
public class JavaGeneric71 {
public static void main(String[] args) {
System.out.println(new Integer(1).getClass() == new Integer(3)
.getClass());
System.out.println(new Integer(1).getClass() == new Double(3)
.getClass());
System.out.println(new ParentClass().getClass() == new ChildClass()
.getClass());
}
}
结果是
true
false
false
新问题是
List
List
System.out.println(l1.getClass() == l2.getClass());
的结果是什么。
正确结果是true
每个泛型类都是一个运行时类
比如
List
List
Collection
System.out.println(l1.getClass() == l2.getClass());
System.out.println(l1.getClass() == c3.getClass());
的结果是两个true
true
true