Chinaunix首页 | 论坛 | 博客
  • 博客访问: 141198
  • 博文数量: 54
  • 博客积分: 2682
  • 博客等级: 少校
  • 技术积分: 580
  • 用 户 组: 普通用户
  • 注册时间: 2009-10-24 20:56
文章分类
文章存档

2012年(2)

2011年(10)

2010年(28)

2009年(14)

我的朋友

分类: 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 = new ArrayList();

c.add(new 小刀());

c.add(new 菜刀());

仓库.add一类组件("刀",c);

Collection k = 仓库.get一类组件(刀).getParts();

}

}

两段代码的差别只在于加了与否。

 

好了,到现在为止我们已经模拟出了一个泛型代码来使用非泛型代码的例子了。

会出现一个问题。

就是Collection的参数会传入Collection的形参

具体发生在

仓库.add一类组件("刀",c); //这里c是 Collection类型

调用

public static void add一类组件(String 这类组件的名字, Collection parts) { ... }

的时候

 

那么这里就会出现未检查错误。相当于

Collection parts = new Collection(); //这个问题上个笔记讲过,这个只是警告,没有错误,parts可以add(new Object());或者add(new Double(1.0));

这里会出现危险操作,因为parts可能加入非Part实现类进入容器。

 

还有一个问题。

就是Collection k = 仓库.get一类组件(刀).getParts();

这句话相当于,

Collection k = new Collection(); //这个问题上个笔记也讲过,这个也只是警告,没有错误,parts只能add(new Part实现类());

 

以上两个错误只是会引发潜在的代码危险。

当然,如果不允许这样赋值就一切解决了,只是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 ys = new Linked();

List xs = ys;

xs.add(new Integer(0)); //编译会警告不会出错,但是运行时出现ClassCastException

到现在为止,应该可以理解这个。

编译只警告是因为想兼容,运行出错是因为类型不对。

这里有人会问

Collection parts = new Collection();

这个为什么parts能add(new Object());运行时不出错

Collection parts = new 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 ys = new LinkedList();

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 parts) {...}

public static Assembly getAssembly(String name) {...}

}

 

public interface Assembly {

Collection getParts(); // Returns a collection of Parts

}

 

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 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 l1 = new ArrayList();

List l2 = new ArrayList();

System.out.println(l1.getClass() == l2.getClass());

的结果是什么。

正确结果是true

每个泛型类都是一个运行时类

比如

List l1 = new ArrayList();

List l2 = new ArrayList();

Collection c3 = new ArrayList();

System.out.println(l1.getClass() == l2.getClass());

System.out.println(l1.getClass() == c3.getClass());

的结果是两个true

true

true

阅读(654) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~