Java泛型小结(二) 通配符

Java泛型支持通配符,可以单独使用 '?' 来表示任意类型,也可以使用extends关键字表示某一个类或接口的子类,也可以使用super关键字表示某一个类,接口的父类型。

本文对读写操作的分析得出什么时候该使用extends 和 super。

[color=blue][b][u]<? super T>使“读”受到限制[/u][/b][/color]

先来看一个例子:

比如要实例化一个List<? super Integer>的numberList ,我们可以使用Integer, Number和Object来完成,如:

List<? super Integer> numberList = new ArrayList<Integer>();  
List<? super Integer> numberList = new ArrayList<Number>();
List<? super Integer> numberList = new ArrayList<Object>();


从这个例子中可以看出,numberList可能是指向List<Integer>, 可能指向List<Number>, 也可能指向List<Object>, 这样多可能性将会限制“读”操作。

因为,

[list]
[*]我们并不能保证读到的是Integer,因为numberList可能指向List<Number>或者List<Object>。
[*]我们并不能保证读到的是Number,因为numberList可能指向List<Object>。
[*]唯一能保证的的就是我们将得到一个Object或者是Object的子类的一个实例,但是我们并不知道具体的子类是什么。
[/list]

如下,有如下这样的一个get方法,使用了List<? super T>:
 private static <T> T get(List<? super T> list, int index) {

}


那么,我们怎么知道list中存放的内容是什么类型呢? 我们只能知道是T或者T的父类,仅此而已。但T的父类具体是什么,不得而知了。

[b]“读”操作不能使用<? super T>, 而应该使用<? extends T>,[/b] 让我们来看看Collections中的一些方法吧。


/**
* Gets the ith element from the given list by repositioning the specified
* list listIterator.
*/
private static <T> T get(ListIterator<? extends T> i, int index) {
T obj = null;
int pos = i.nextIndex();
if (pos <= index) {
do {
obj = i.next();
} while (pos++ < index);
} else {
do {
obj = i.previous();
} while (--pos > index);
}
return obj;
}


    public static <T> int binarySearch(List<? extends T> list, T key, Comparator<? super T> c) {
if (c==null)
return binarySearch((List) list, key);

if (list instanceof RandomAccess || list.size()<BINARYSEARCH_THRESHOLD)
return Collections.indexedBinarySearch(list, key, c);
else
return Collections.iteratorBinarySearch(list, key, c);
}



[color=blue][u][b]<? extedns T>使“写”受到限制 [/b][/u][/color]

一个List<? extends Number>的numberList可能指向List<Number>, 可能指向List<Integer>, 也可能指向List<Object>.

List<? extends Number> numberList = new ArrayList<Number>();
List<? extends Number> numberList = new ArrayList<Integer>();
List<? extends Number> numberList = new ArrayList<Double>();


这种多可能性将让<? extends T> 的“写”操作受到限制。

因为,

[list]
[*]我们不能添加一个Integer类型的值,因为numberList可能指向List<Double>
[*]我们不能添加一个Double类型的值,因为numberList可能指向的是List<Integer>
[*]我们不能添加一个Number类型的值,因为numberList可能指向的是List<Integer>
[/list]

[color=blue][b]我们不能添加任何对象到List<? extends T>, 那是因为我们并不能保证实际指向的是什么类型的List,所以也就不能保证想要添加的对象是List所允许的类型。[/b][/color]

[color=blue][b]唯一能保证的是只能读取并得到一个T或者是T的子类。[/b][/color]


上面的分析,我们可以得出一个结论, 那就是[color=blue][b]<? extends T> 不适合“写”操作,<? super T> 不适合“读”操作。[/b][/color]

其实,
Collections中的copy方法很好的使用<? extends T> 和 <? super T>的经典案例。

另外还有一个PECS原则供参考:

[b]PECS原则-->[/b]


[img]http://dl2.iteye.com/upload/attachment/0089/1351/57ad1e03-be58-3698-bfef-5e991f3dacbd.jpg[/img]

在 Collections#copy方法中,src ([b]the producing list[/b])使用[color=blue][b]extends[/b][/color], 而 desc ([b]the consuming list[/b]) 使用[color=blue][b]super[/b][/color]. 代码如下:

    public static <T> void copy(List<? super T> dest, List<? extends T> src) {
int srcSize = src.size();
if (srcSize > dest.size())
throw new IndexOutOfBoundsException("Source does not fit in dest");

if (srcSize < COPY_THRESHOLD ||
(src instanceof RandomAccess && dest instanceof RandomAccess)) {
for (int i=0; i<srcSize; i++)
dest.set(i, src.get(i));
} else {
ListIterator<? super T> di=dest.listIterator();
ListIterator<? extends T> si=src.listIterator();
for (int i=0; i<srcSize; i++) {
di.next();
di.set(si.next());
}
}
}


[color=blue][b][u]通配符<?>的使用[/u][/b][/color]

List<?>表示的是任意类型。因为编译器不知道List中容纳的是什么类型的元素,所以不能对其进行增加,修改的操作。 但是,List<?>拥有删除的功能,因为这些功能与泛型类型没有关系。

所以,List<?>适合用于与泛型类型无关的方法,比如remove, shuffle等。

我们来看看Collections中的几个方法吧:

public static void shuffle(List<?> list, Random rnd) {
int size = list.size();
if (size < SHUFFLE_THRESHOLD || list instanceof RandomAccess) {
for (int i=size; i>1; i--)
swap(list, i-1, rnd.nextInt(i));
} else {
Object arr[] = list.toArray();

// Shuffle array
for (int i=size; i>1; i--)
swap(arr, i-1, rnd.nextInt(i));

// Dump array back into list
ListIterator it = list.listIterator();
for (int i=0; i<arr.length; i++) {
it.next();
it.set(arr[i]);
}
}
}



    public static void swap(List<?> list, int i, int j) {
final List l = list;
l.set(i, l.set(j, l.get(i)));
}


    public static void rotate(List<?> list, int distance) {
if (list instanceof RandomAccess || list.size() < ROTATE_THRESHOLD)
rotate1(list, distance);
else
rotate2(list, distance);
}


[b]总结:[/b]

[list]
[*][color=blue][b]只用于“读”功能时,泛型结构使用<? extends T>
[*]只用于“写”功能时,泛型结构使用<? super T>
[*]如果既用于“写”,又用于“读”操作,那么直接使用<T>.
[*]如果操作与泛型类型无关,那么使用<?>[/b][/color]
[/list]
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值