大概有限制通配符的使用是源于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);
}
}
到这里,有限制通配符类型就大致叙述到这里了,实际应用中可能会遇到非常复杂的应用,到时候就要具体问题具体分析了,我在工作中基本上用不到,也谈不上有什么经验了。
分享到:
相关推荐
Java 第二阶段提升编程能力【泛型】---- 代码 Java 第二阶段提升编程能力【泛型】---- 代码 Java 第二阶段提升编程能力【泛型】---- 代码 Java 第二阶段提升编程能力【泛型】---- 代码 Java 第二阶段提升编程能力...
全面总结Java泛型--实例
个人制作且上课使用的课件,希望对大家初步了解泛型相关内容有一定的帮助。个人制作且上课使用的课件,希望对大家初步了解泛型相关内容有一定的帮助。
【Java基础】泛型方法 - 右撇子 - 博客频道 - CSDN.NET
泛型是Java SE 1.5的新特性,泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。这种参数类型可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口、泛型方法。 Java语言引入泛型的好处是...
补充知识:泛型1---马克-to-win java视频的详细描述与介绍
Delphi泛型库--DGL(The Delphi Generic Library) 在Delphi中实现的类型安全、高效、易用的泛型容器和算法库;借鉴了C++标准中的STL;
4.java泛型的限制.zip4.java泛型的限制.zip4.java泛型的限制.zip4.java泛型的限制.zip4.java泛型的限制.zip4.java泛型的限制.zip4.java泛型的限制.zip4.java泛型的限制.zip4.java泛型的限制.zip4.java泛型的限制.zip...
Java泛型编程指南.pdf 此文章译自SUN的泛型编程指南
java泛型深入.pdf
Java Generics and Collections 英文版,详细描述java 泛型技术
java基础-泛型通配符
* 一个参数通配符的实例 * 说明:对一个包含了数值元素的集合进行汇总运算。在这种情况下,用户并不关心 * 集合中的每一个对象是什么类型,只要它是数值型即可,而且,用户也希望集合中可以 * 存放不同类型的数值...
这是一个使用JAVA实现的泛型编程,分为两部分,第一部分创建泛型类,并实例化泛型对象,得出相加结果。 第二部分用户自行输入0--4,选择要进行的加减乘除运算或退出,再输入要进行运算的两个数,并返回运算结果及...
补充知识2 ---马克-to-win java视频泛型的详细描述与介绍
java,学习java泛型,java培训之泛型.pptxjava培训之泛型.pptxjava培训之泛型.pptxjava培训之泛型.pptx
Java泛型和集合]-英文版
java 泛型接口示例 java 泛型接口示例 java 泛型接口示例
java泛型源码Java泛型用法 步骤1 原始类型有问题。 第2步 使用泛型类型。 第三步 车库和车辆。 原始类型。 第四步 首先尝试生成车库。 木星在我的车库里。 第5步 泛型上限。 第6步 TripleGarage 步骤7 试图使用泛型...
泛型应用--图的深度(广度)优先遍历.成语接龙例