首页 > 开发 > 综合 > 正文

J2SE5.0 实例---泛型

2024-07-21 02:14:11
字体:
来源:转载
供稿:网友
泛型简介


泛型其实并不是一种新的语言元素,c++中早就就有,但是在c++之后的java却没有吸收这个特性,现在java也有了泛型的特性,大概也和.net的竞争有关系吧。

首先看泛型的一个应用。

在过去,我们可能经常要写一些类似这样的代码:

list stringlist=new linkedlist();

stringlist.add("firststring");

stringlist.add("secondstring");

string str=(string)stringlist.iterator().next();



实际上第三行对string的类型转换意义并不大,因为通常我们如果在操作一个list,都是知道这个list里面放的是什么类型对象的,但是我们如果不这样写又通不过语法检查。

利用java的泛型机制,我们可以这么写:

list<string> stringlist=new linkedlist<string>();

stringlist.add("firststring");

stringlist.add("secondstring");

string str=stringlist.iterator().next();

这样做的好处是在定义容器的时候就指明了容器中的类型,一方面我们不再需要取一个元素时候做强制类型转换,另外一方面如果在这个容器中放入的对象类型不符合要求,那么会在编译时候产生一个错误,而不是在运行时候才抛出一个异常。

另外这样也提高了程序的可读性。
泛型类型的定义
下面是一个简单的使用泛型类的定义:

public class mygenericclass<t> {

  private t value;


 


  public t getvalue() {

     return value;

  }

 

  public void setvalue(t value) {

     this.value = value;

  }  

}

值得注意的一点是,静态变量不能够使用泛型定义,也就是说类似下面的语句是非法的:



public class mygenericclass<t> {

  public static t value;//错误的定义

}

此外,泛型的定义不会被继承,举个例子来说,如果a是b的子类,而c是一个声明了泛型定义的类型的话,c<a>不是c<b>的子类。为了更好的说明,可以看下面的代码,这段代码是错误的。

list<string> strlist =new arraylist<string>();

list<object> objlist=strlist;  //错误的赋值



不过这样一段代码是正确的:

list<object> strlist =new arraylist<object>();

strlist.add("jsdkfjsdl");
统配类型
假设我们需要这样一个函数,使用它可以把一个集合中所有的元素打印出来,在以前我们可能这样定义:

void printcollection(collection c) {

  iterator i = c.iterator();

  for (k = 0; k < c.size(); k++)

  {

     system.out.println(i.next());

  }

}

使用新的泛型特性我们可能会这样写:

void printcollection(collection<object> c)

{

  for (object e : c)

  {

     system.out.println(e);

  }

}

但是这样有一个问题,假如我们现在有个对象类型是collection<string>,那么我们不能够将它作为参数传给printcollection,因为collection<string>并不是collection<object>的子类。

为了解决这个问题,我们可以使用统配类型?,也就是定义成下面这个样子:

void printcollection(collection<?> c)

{

  for (object e : c)

  {

     system.out.println(e);

  }

}

可以说collection<?>是所有collection的父类。

再来看一段下面的代码

private void clearallmaps(collection<map> c)

{

     for(map m:c)

     {

         m.clear();

     }

}

毫无疑问,它也存在上面我们所说的问题,也就是对hashmap之类map的子类无法进行操作,但是如果我们将参数改成collection<?>又不大合理,因为我们只希望对父类为map的子类进行操作,那么我们可以这样改写:

private void clearallmaps(collection<? extends map> c)

{

  for(map m:c)

  {

     m.clear();

  }

}

类似于? extends map之类的统配符称为限定统配类型。

假设一个对象h类型为collection<hashmap>,那么我们将h作为参数传给clearallmaps,如下面一段代码所示:

list<hashmap<string,string>> h=new arraylist<hashmap<string,string>>();

hashmap<string,string> m=new hashmap<string,string>();

m.put("key","value");

h.add(m);

clearallmaps(h);

对于在类似于上面所说,使用了? extend xxx的方法,值得注意的一点是不能够在方法体内用xxx的子类对象作为代替。如下面一段代码是错误的:

public void addrectangle(list<? extends shape> shapes)

{

        shapes.add(0, new rectangle()); // 错误用法!

}

这里我们假设rectangle是shape的一个子类。

不允许这样写的原因比较简单,因为调用该方法时候参数类型可能是shape的另外一个子类。假如说shape除了rectangle这个子类以外还有另外一个子类circle,那么我们可以把一个list<circle>类型的对象作为参数传给这个方法(注意这样是合法的),而在方法体内却把一个rectangle对象放到了shapes里面,这显然是不合理的。

除了extends,在泛型参数类型中还可以使用super关键字,参照下面一段程序:

private void addstring(collection <? super string> c)

{

         c.add("a string");

}


泛型函数
我们在前面提到了统配类型,现在让我们来设想一个函数,它实现这样的功能,将一个数组中的元素添加到一个collection中,为了保证程序的通用性,我们可能会写出另外一段错误的代码:

private void fromarraytocollection(object[] a, collection<?> c)

{

        for (object o : a)

        {

         c.add(o); // 错误的代码

        }

}

那么这个函数应该怎么写呢?我们可以通过对函数添加泛型参数的方法实现,如下面所示:

private <t> void  exfromarraytocollection(t[] a,  collection<t> c)

{

         for (t o : a)

         {

            c.add(o); //这样是正确的

         }

}


 


那么,在什么时候我们应该使用统配类型,什么时候我们应该使用泛型函数呢?答案是取决于函数参数之间,函数参数和返回值之间的类型依赖性。

如果一个函数的参数类型与函数返回的参数没有必然关联,同时对于该函数其他的参数的类型也没有依赖关系,那么我们就应该使用统配符,否则就应该使用泛型函数。

为了更清楚地说明这一点,我们可以看一下java.util包中collections类型几个方法的定义:

class collections {

        static void swap(list<?> list, int i, int j) {...}

        static <t> void  copy                                 (list<? super t> dest, list<? extends t> src)    {...}

}

其中swap函数实际上也可以这样定义:

static <t>void swap(list<t> list, int i, int j) {...}

但是注意到这里泛型类型参数t只在参数中用到了一次,也就是说它和函数其他部分没有依赖性,这可以看作是我们应该使用?的一个标志。

copy方法中,拷贝源src中的元素必须是dest所能够接受的,src中的元素必须是t的一个子类,但是具体它是哪种子类我们又不必关心,所以方法中使用了泛型作为一个类型参数,同时也用了统配类型作为第二类型参数
  • 本文来源于网页设计爱好者web开发社区http://www.html.org.cn收集整理,欢迎访问。
  • 上一篇:Threads

    下一篇:hibernate in 查询

    发表评论 共有条评论
    用户名: 密码:
    验证码: 匿名发表