JDK5.0新特性之:泛型

JDK5.0新特性之:泛型

文/陈刚 2005-11-09 

一、前言

  泛型这个词在现在的JAVA挺时髦,光从字面上你是无法知道它代表些什么东东的,所以我们还是不要从字面去理解,而是从一些实例去了解它吧。

二、泛型之前的日子

  JDK1.4之前是没有泛型的概念的,所以我们才会有下面的代码:

        List list = new ArrayList();
list.add("aaaa");
list.add("bbbb");
list.add("cccc");
        for (Iterator it = list.iterator(); it.hasNext();) {
String str = (String) it.next();
System.out.println(str);
}

  上面是一段很平常的代码,在一个List集合加入一些字符串,然后再用一个遍历循环把它打印出来。“String str = (String) it.next()”这一句我们可以看到List取出值都是Object,所以我们要得String型,还要做一个类型转换,真是麻烦。更麻烦的是list.add(Object obj)的参数是Object类型,所以如果我们一不小心把list.add("cccc");写成list.add(new Integer(76));程序在循环打印的类型转换中就会出错。

  问题:我们能不能让add方法只认String型呢?
  回答:可以!用JDK5.0的泛型。

三、泛型后的幸福生活

  JAVA有了泛型后,就象十年的老光棍讨了老婆,那个好处自不待言。我们来看看上面的例子改成泛型的写法是怎么样的:

        List<String> list = new ArrayList<String>();
list.add("aaaa");
list.add("bbbb");
list.add("cccc");
        for (Iterator<String> it = list.iterator(); it.hasNext();) {
String str=it.next();
System.out.println(str);
}

  看到差别了吗?泛型其实很简单,就是在定义类型的后面加上"<类型>"这样子的声明就行了,它主要还有以下差别:

  • list.add方法只能接受String类型。list.add(new Integer(76))这样的语句不需要运行程序,在编译时就会检查通不过。
  • it.next()的返回值不再是Object,而变成了String

  当然我们其实在循环部份也可以象下面这么写,是不是简洁了很多呢 :-)

        List<String> list = new ArrayList<String>();
list.add("aaaa");
list.add("bbbb");
list.add("cccc");
        for (String str : list) {
System.out.println(str);
}

  当然需要说明的是,List不仅可以List<String>,也可以是List<Integer>等等其他任何类型。

四、更深入了解泛型

(1)层层推进的泛型声明

  “List<List> list;”表示什么呢?就是只接收List型的参数,比如:

        List<List> list = new ArrayList<List>();
        list.add(new ArrayList());
        list.add(new Vector());
        list.add(new LinkedList());

  这里要注意List是接口,ArrayList、Vector、LinkedList都是这一接口下的实现类。下面这个有点怪异了,“List<List<String>> list;”表示它只接受List型的参数,而且这种List型的参数又是只是只接受String型,有点层层推进的味道在里面了。

        List<List<String>> list = new ArrayList<List<String>>();
        list.add(new ArrayList<String>());
        list.add(new Vector<String>());
        list.add(new LinkedList<String>());

(2)使用泛型上限通通配符:extends

  这里要着重强调一点:变量的泛型声明和方法的参数的泛型声明有很大差别。

  变量声明成某类型,同时也可以接受它的子类。比如说Integer、Long、Float都是抽象类Number的子类,所以下面的代码一点问题也没有:

        List<Number> list = new ArrayList<Number>();
        list.add(new Integer(1));
        list.add(new Long(1));
        list.add(new Float(1.2));

  但如果换成方法参数的泛型声明则要严格得多了:子类也是不行的。比如下面的代码就是错误的,因为printList参数只接受Number值的List,就是是Number子类的Integer值的List也不行。

    public static void main(String[] args) {
        List<Integer> list = new ArrayList<Integer>();
        list.add(new Integer(1));
        list.add(new Integer(2));
        printList(list);
    }
   
    private static void printList(List<Number> list){
        for (Number num : list) {
            System.out.println(num);
        }
    }

 上面代码修改的方法有两个,如下

修改方法一:改变量的泛型声明
  将 List<Integer> list = new ArrayList<Integer>();
  改为 List<Number> list = new ArrayList<Number>();

修改方法二:用界限通配符改方法参数的泛型声明
  将 printList(List<Number> list)
  改为 printList(List<? extends Number> list)
  说明:extends 的含义就是表示参数可以接受Number型的子类。

(3)使用泛型下限通通配符:super

    在上限就有下限,下限行就是super,用法和extends一样,含义则和extends相反。比如printList(List<? super Integer> list)表示参数可以接受Integer型及Integer型的超类,即Number了,当然也包括Object这个顶级类。

(4)配置符:?

  ?表示可以接受任何类型,不过我觉得它用得不多,因为printList(List<?> list)和printList(List list)的作用是一样的。

五、创建一个支持泛型的类

(1)创建一个泛型的类

public class Point<T> {
    T x;
    T y;
    public T getX() {
        return x;
    }
    public T getY() {
        return y;
    }
    public void setX(T x) {
        this.x = x;
    }
    public void setY(T y) {
        this.y = y;
    }
}

  使用这个类的代码如下:

        Point<Integer> p = new Point<Integer>();
        p.setX(new Integer(1));
        p.setY(new Integer(2));
       
        Point<String> b = new Point<String>();
        b.setX("1");
        b.setY("2");

  说明:在Point<T>的定义中,T并非关键字,你也可以这样定义Point<ABC>,当然一般还是写T吧,简单也规范。

(2)泛型类的继承与实现

  java.util.Comparator类是JDK里用来排序的,其源代码如下:

package java.util;
public interface Comparator<T> {
    int compare(T o1, T o2);
    boolean equals(Object obj);
}

   一个实现此接口的类如下:

    public class MyComparator<T> implements Comparator<ObjectInstance> {
        public int compare(ObjectInstance o1, ObjectInstance o2) {
            String s1 = o1.getObjectName().getCanonicalName();
            String s2 = o2.getObjectName().getCanonicalName();
            return s1.compareToIgnoreCase(s2);
        }
    }

  说明:ObjectInstance可能大家还太明白,这是我实际项目中的一段代码(关于JMX的),ObjectInstance全称javax.management.ObjectInstance。MyComparator的使用代码如下:

Set set = ......(省略)
List<ObjectInstance> mbeans = new ArrayList<ObjectInstance>(set);
Collections.sort(mbeans, new MyComparator<ObjectInstance>());

六、最后的感言

  JAVA有了泛型就象老光棍讨了老婆,好处大大的,但和女人一样麻烦也跟着来了:它的严格类型检查,使隐藏的BUG更少。有些地方确实也使代码简洁了,有些地方却会使得代码更复杂。所以运用之妙在于是否用得适当,尽量把泛型往简单里用,别越搞越复杂了。 

参考资料

J2SE 5.0中的泛型 http://www.matrix.org.cn/resource/article/43/43634_java_generics.html

作者简介

陈刚,广西桂林人,著作有《Eclipse从入门到精通》
您可以通过其博客了解更多信息和文章:http://www.ChenGang.com.cn

你可能感兴趣的:(JDK5.0新特性之:泛型)