`
zddava
  • 浏览: 240732 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

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

阅读更多
大概有限制通配符的使用是源于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);

	}

}


到这里,有限制通配符类型就大致叙述到这里了,实际应用中可能会遇到非常复杂的应用,到时候就要具体问题具体分析了,我在工作中基本上用不到,也谈不上有什么经验了。
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics