您的位置:首页 > 编程语言 > Java开发

深入了解Java泛型(四) -- 有限制通配符

2009-05-10 02:03 246 查看
大概有限制通配符的使用是源于Java的泛型的不可变性,所谓的不可变性就是说对于两个Set<T1>和Set<T2>,不管T1和T2谁是谁的父类,Set<T1>和Set<T2>都不会是父子类的关系。

Java泛型的不可变性在应用中可能会遇到一些不方便的地方,尽管它很安全,比如一段这样的代码,加入我们要自己实现一个简单的List类如下:

public class MyList<T> {

private T[] elements = null;

private int cursor = -1;

private static final int DEFAULT_CAPACITY = 10;

public MyList() {
this(DEFAULT_CAPACITY);
}

public MyList(int capacity) {
@SuppressWarnings("unchecked")
T[] t = (T[]) new Object[capacity];
elements = t;
}

private void allocateNew() {
@SuppressWarnings("unchecked")
T[] t = (T[]) new Object[elements.length * 2];
for (int i = 0; i < elements.length; i++) {
t[i] = elements[i];
}
elements = t;
}

public MyList<T> add(T t) {
if (cursor >= elements.length - 1) {
allocateNew();
}

cursor++;
elements[cursor] = t;
return this;
}

public T get(int i) {
return elements[i];
}

public int size() {
return elements.length;
}

public MyList<T> addAll(MyList<T> myList) {
int size = myList.size();
for (int i = 0; i < size; i++) {
add(myList.get(i));
}
return this;
}
}


很简单,内部维护一个数组去实现列表,下面是使用这个类的代码:

public class InvariantTester {

public static void main(String[] args) {
MyList<CharSequence> charSeqList = new MyList<CharSequence>();
charSeqList.add("s").add("t").add("r").add("i").add("n").add("g");

MyList<String> stringList = new MyList<String>();
stringList.add("s").add("t").add("r").add("i").add("n").add("g");

charSeqList.addAll(stringList);

}

}


这里前面的charSeqList.add("s").add("t").add("r").add("i").add("n").add("g")这步是不会报错的,而后边的charSeqList.addAll(stringList)是通不过编译的,原因就是泛型的不可变性,这里就要通过有限制的通配符类型去解决了,很简单,修改一下addAll的方法声明就可以了:

public MyList<T> addAll(MyList<? extends T> myList) {
int size = myList.size();
for (int i = 0; i < size; i++) {
add(myList.get(i));
}
return this;
}


这里就是有限制通配符类型的一个典型应用,当然还可以使用<? super T>这种限制方式的。

为了在泛型的方法参数上获得最大限度的灵活性,就需要有限制通配符类型的参与了,这里需要重点介绍的是使用有限制通配符类型时的PECS(producer-extends, consumer-super)原则,也就是<? extends T>和<? super T>的使用时机选择的原则。

如果类型是一个生产者,那么就使用extends,如果是消费者,那么就用super。

比如前面的例子,addAll(MyList<? extends T> myList)这个方法的参数是为了给MyList类消费的,所以参数是生产者,下面再举个消费者的例子。还是上边的MyList类,我再添加一个方法进去:

public void copyTo(MyList<? super T> dstList) {
dstList.addAll(this);
}


基本就是addAll()方法的逆方法,这里的参数dstList就是一个消费者了,同样,稍稍修改一下InvariantTester,加入这个方法的使用:

public class InvariantTester {

public static void main(String[] args) {
MyList<CharSequence> charSeqList = new MyList<CharSequence>();
charSeqList.add("s").add("t").add("r").add("i").add("n").add("g");

MyList<String> stringList = new MyList<String>();
stringList.add("s").add("t").add("r").add("i").add("n").add("g");

charSeqList.addAll(stringList);

stringList.copyTo(charSeqList);

}

}


到这里,有限制通配符类型就大致叙述到这里了,实际应用中可能会遇到非常复杂的应用,到时候就要具体问题具体分析了,我在工作中基本上用不到,也谈不上有什么经验了。 阅读更多
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: