Java

本类阅读TOP10

·使用MyEclipse开发Struts框架的Hello World!(录像1)
·hibernate配置笔记
·AOP编程入门--Java篇
·linux下Tomcat 5.0.20 与 Apache 2 安装/集成/配置
·在win2003下整合了整合Tomcat5.5+ apache_2.0.53+ mod_jk_2.0.47.dll
·构建Linux下IDE环境--Eclipse篇
·Jsp 连接 mySQL、Oracle 数据库备忘(Windows平台)
·ASP、JSP、PHP 三种技术比较
·Tomcat5.5.9的安装配置
·AWT GUI 设计笔记(二)

分类导航
VC语言Delphi
VB语言ASP
PerlJava
Script数据库
其他语言游戏开发
文件格式网站制作
软件工程.NET开发
J2SE5.0新特性之范型编程

作者:未知 来源:月光软件站 加入时间:2005-6-5 月光软件站

本章主要参考sun公司文档。

 

C++程序员对范型编程肯定不陌生,尤其在STL大行其道的时候,C#2.0也将实现范型编程的功能。Java也不甘示弱,也推出了范型编程的语言新特性。

 

1.一个简单的范型示例

在以前,你可能遇到过这样的代码:
  1. List list = new LinkedList();
  2. list.add(“麻省理工”);
  3. list.add(“普林斯顿”);
  4. list.add(“伯克利”);
  5. String name = (String)list.iterator.next();
  6.  


注意,第三行需要强制转换。而使用范型:
  1. List<String> list = new LinkedList<String>();
  2. list.add(“麻省理工”);
  3. list.add(“普林斯顿”);
  4. list.add(“伯克利”);
  5. String name = list.iterator.next();
  6.  

这里将list声明成String类型的List。List是有一个类型参数的范型接口。这个例子中类型参数是String。

 

2.定义简单的范型

看j2se5.0中List和Iterator接口的实现(片断):
  1. public interface List<E> 
  2. void add(E x);
  3. Iterator<E> iterator();
  4. }
  5. public interface Iterator<E> 
  6. E next();
  7. boolean hasNext();
  8. }
  9.  
  10.  

上面的代码我们比较熟悉,但是其中增加了尖括号。尖括号中的内容定义了接口List和Iterator的形式类型参数。类型参数可以用在范型声明中,如类和接口的声明。

一旦声明了范型,你就可以使用它。在上面的例子中使用了List<String>。这里使用String是实参,代替了形参E。如果使用List<Integer>,则用实参Integer代替了形参E。

不管List<Integer>还是List<String>,它们的类只有一个。考虑下面的代码:
  1. List<String> list1 = new LinkedList<String>();
  2. List<Integer> list2 = new LinkedList<Integer>();
  3. System.out.println(list1.getClass()==list2.getClass());
 

输出结果为true。

 

一般来说,形式类型参数都是大写,尽量使用单个字母,许多容器类都使用E作为参数。

 

3.范型和继承

考虑下面的代码,你认为它会出错吗?
  1. String s = “smallnest@163.com”;
  2. Object o = s:
  3.  

当然,String类继承Object类,这样做不会出错。但下面的代码呢?
  1. List<String> s = new LinkedList<String>();
  2. List<Object>o=s;
  3.  


编译出错!

是的,List<Object>和List<String>没有继承关系。

 

4.通配符

考虑下面一个方法:
  1. public void printCollection(Collection<Object> c)
  2. {
  3.    for(Object o:c)
  4. {
  5.    System.out.printf(“%s%n”,o);
  6. }
  7. }
  8.  

事实上,上面这个方法并不通用,它只能打印Collection<Object>类型的集合,象其他的如Collection<String>、Collection<Integer>并不能被打印,因为对象类型不一致。

为了解决这个问题,可以使用通配符:
  1. public void printCollection(Collection<?> c)
  2. {
  3.    for(Object o:c)
  4. {
  5.    System.out.printf(“%s%n”,o);
  6. }
  7. }
  8.  

Collection<?>被称作未知类型的集合。问号代表各种类型。

上面的读取集合中的数据时,我们采用Object类型。这样做时可以的,因为不管未知类型最终代表何种类型,它的数据都继承Object类,那么再考虑一下下面的代码:
  1. Collection<?> c = new ArrayList<String>();
  2. c.add(new Object());  //!!!!
  3.  

这样做时错误的,因为我们不知道?代表何种类型,所以我们不能直接将Object增加到集合中,这会出现类型不匹配的情况。

 

5.有限制的通配符

考虑下面的代码
  1. class Man
  2. {
  3.    public String name = “”;
  4. }
  5. class GoodMan extends Man
  6. {
  7.    public String name = “”
  8. }
  9. class BadMan extends Man
  10. {
  11.    public String name = “”
  12. }
  13.  
  14.  

考虑下面的范型方法:
  1. public void printName(List<Man> men)
  2. {
  3.    for(Man man:men)
  4.    {
  5.       System.out.println(“姓名:” + man.name);
  6.    }
  7. }
  8.  


这个范型方法只能显示List<Man>类型的数据,下面的代码允许显示Man和它的子类。
  1. public void printName(List<? extends  Man> men)
  2. {
  3.    for(Man man:men)
  4.    {
  5.       System.out.println(“姓名:” + man.name);
  6.    }
  7. }
  8.  

这里使用? extends Man代替Man,表明接受任何Man的子类做为参数。

和前面的代码类似,下面的代码也是不正确的:
  1. public void adman(List<? extends Man> men)
  2. {
  3.    GoodMan good = new GoodMan();
  4.    good.name = “晁岳攀”;
  5.    men.add(good);
  6. }
  7.  

原因也很简单,因为?代表一切继承Man的类,你并不能保证就一定时GoodMan类。

 

和这种用法类似:
  1. public void adman(List<? super GoodMan> men)
  2. {
  3.    GoodMan good = new GoodMan();
  4.    good.name = “晁岳攀”;
  5.    men.add(good);
  6. }
  7.  
  8.  

6.范型方法

考虑下面的代码,我们将一个数组的内容加到一个集合中
  1. public  void copyArrayToCollection(Man[] men, Collection<?>c)
  2. {
  3. for(Man man:men)
  4. {
  5.    c.add(man);
  6. }
  7. }
  8.  

这段代码时错的!

因为我们并不知道集合C的类型,所以不能将Man类型的数据加到集合中。

可以使用范型方法解决:
  1. public <T> void copyArrayToCollection(T[] men, Collection<T>c)
  2. {
  3. for(T man:men)
  4. {
  5.    c.add(man);
  6. }
  7. }
  8.  
  9.  

这里T时一个形式类型参数。

何时该采用通用方法?何时该采用通配符?

考虑下面的例子:
  1. interface  Collection<E>
  2. {
  3.   public boolean containsAll(Collection<?> c);
  4. public boolean addAll(Collection<? extends E> c);
  5. }
  6.  

改写成通用方法
  1. interface  Collection<E>
  2. {
  3.    public <T> boolean containsAll(Collection<T> c);
  4. public <T extends E> boolean addAll(Collection<T> c);
  5. }
  6.  

然而,在这里每个方法T只使用了一次,返回值不依赖形式参数,其他参数也不依赖形式参数。这说明实参被用作多态,这种情况下就应该用通配符。




相关文章

相关软件