泛型(二)-类型参数通配符

   假设我们需要写一个方法来显示一个List中的元素。在以前,我们只需要象这样写段代码:

public static void printList(PrintWriter out, List list) {
    for(int i=0, n=list.size(); i < n; i++) {
        if (i > 0) out.print(", ");
        out.print(list.get(i).toString());
    }
}

 

     在Java5.0中,List是一个泛型类型,如果我们试图编译这个方法,我们将会得到unchecked警告。为了解决这些警告,我们这样来修改这个方法:

public static void printList(PrintWriter out, List<Object> list) {
    for(int i=0, n=list.size(); i < n; i++) {
        if (i > 0) out.print(", ");
        out.print(list.get(i).toString());
    }
}

   这段代码能够编译通过同时不会有警告,但是它并不是非常地有效,因为只有那些被声明为List<Object>的list才会被允许使用这个方法。类似于List<String>和List<Integer>这样的List并不能被转型为List<Object>。事实上我们需要一个类型安全的printList()方法,它能够接受我们传入的任何List,而不关心它被参数化为什么。解决办法是使用类型参数通配符。方法可以被修改成这样:

public static void printList(PrintWriter out, List<?> list) {
    for(int i=0, n=list.size(); i < n; i++) {
        if (i > 0) out.print(", ");
        Object o = list.get(i);
        out.print(o.toString());
    }
}

       这个版本的方法能够被编译过,没有警告,而且能够在任何我们希望使用的地方使用。通配符“?”表示一个未知类型,类型List<?>被读作“List of unknown”

作为一般原则,如果类型是泛型的,同时我们并不知道或者并不关心值的类型,应该使用“?”通配符来代替一个未经处理的类型。未经处理的类型被允许仅是为了向下兼容,而且应该只能够被允许出现在老的代码中。注意,无论如何,不能在调用构造器时使用通配符。下面的代码是非法的:

List<?> l = new ArrayList<?>();

        创建一个不知道类型的List是毫无道理的。如果创建了它,那么我们必须知道它将保持的元素是什么类型的。可以在随后的方法中不关心元素类型而去遍历这里list,但是需要在创建它的时候描述元素的类型。如果确实需要一个List来保持任何类型,那么只能这么写:

List<Object> l = new ArrayList<Object>();

      从上面的printList()例子中,必须要搞清楚List<?>既不是List<Object>也不是一个未经处理的List。一个使用通配符的List<?>有两个重要的特性。
         第一,考察类似于get()的方法,他们被声明返回一个值,这个值的类型是类型参数中指定的。在这个例子中,类型是“unknown”,所以这些方法返回一个Object。既然我们期望的是调用这个object的toString()方法,程序能够很好的满足我们的意愿。
  第二,考察List的类似add()的方法,他们被声明为接受一个参数,这个参数被类型参数所定义。出人意料的是,当类型参数是未确定的,编译器不允许调用任何有不确定参数类型的方法——因为它不能确认是否传入了一个恰当的值。一个List(?)实际上是只读的——编译器不允许我们调用类似于add(),set(),addAll()这类的方法。

你可能感兴趣的:(通配符)