我们有一个Stack类
public Stack{
//有如下方法
public void put(E data);
public E pop();
}
我们想增加能够将容器中的所有数据存储到栈中的方法:
putAll(IteratorStack类的完整实现:
public class GenericsStack {
private static final int DEFAULT_SIZE = 16;
//该行出现了错误,无法创建泛型数组
private Object[] objects = new Object[DEFAULT_SIZE];
private int count = 0;
public void put(E obj){
expandArray();
objects[count] = obj;
++count;
}
private void expandArray(){
if(count == objects.length){
//扩充容量
objects = Arrays.copyOf(objects, 2*count);
}
}
public E pop(){
//首先判断是否数组为空
if (!isEmpty()){
@SuppressWarnings("unchecked")
E obj = (E)objects[count];
--count;
return obj;
}
return null;
}
private boolean isEmpty(){
if (count == 0){
return true;
}
else {
return false;
}
}
//获取迭代器
public void putAll(Iterator iterator){
while(iterator.hasNext()){
E e = iterator.next();
put(e);
}
}
}
使用:
List numbers = new ArrayList();
GenericsStack stack = new GenericsStack();
stack.putAll(numbers.iterator());
既然是Number的栈,那么应该可以存储Number的子类,Integer才是。没错使用Stack的put(E data)方法确实可以。
那么表示putAll(Iterator
但是由于泛型的不可变性,Iterator
List numbers = new ArrayList();
GenericsStack stack = new GenericsStack();
stack.putAll(numbers.iterator());//存入了Iterator,是会报错的
是错误的。那么显然这个方法是不完整的。
那么怎么能够让putAll(Iterator
将输入参数中的Iterator
public void putAll(Iterator extends E> iterator)
因为Integer是Number的子类,所以成立,这就不会报错了。
既然我们有了让容器的数据,全部放到Stack中的方法,那么应该还有一个能够让Stack类内的数据全部放到容器中的方法。
public void popAll(Collection collection){
E data = pop();
if (data != null){
collection.add(data);
}
}
然后我们来调用这个方法
//使用正确
Collection collection = new ArrayList();
stack.popAll(collection);
//使用错误
Collection
讲道理:Number的父类是Object,那么Number实例是能够存放到Collection所以我们需要改进。既然Object是Number的父类,我们将输入参数Collection
public void popAll(Collection super E> collection)
表示接受:为E的父类的容器。
当参数化类型(?)表示E的生产者的时候,用 extends E>
比如:上述例子中,putAll()的iterator参数,产生E的实例到类中,供Stack使用
当参数化类型(?)表示E的消费者的时候,用 super E>
比如:popAll()中的collection参数,将Stack中E的实例装入到Collection中
举个复杂的例子:我们之前写过一个将容器中的数据相减的方法
public T reduce(List list,Function function,T initalVal){
Iterator iterator = list.iterator();
T result = initalVal;
//相减
while (iterator.hasNext()){
result = function.apply(result,iterator.next());
}
return result;
}
这个方法中,ListFunction
但是如果该方法的功能不是减法,而是Function方法的辅助类的话就不一样了,意思就是,该方法的功能是根据Function来确定的。那么就不能够判断Function是消费者还是生产者了。这个时候由于不能判断,那么就应该保持不变。还是Function
改进第二十七条的union方法,我们先看一下原先的方法
//先看下原先的泛型方法
public Set union(Set set1,Set set2){
Set set = new HashSet(set1);
set.addAll(set2);
return set;
}
然后我们发现,两个Set输入参数,其实都是生产者,也就是都是添加E到该类中,那么就应该改进成
public
使用:
public static void main(String[] args){
Set intSet = new HashSet();
Set doubleSet = new HashSet();
Set numberSet = unionSet(intSet, doubleSet);//报错
}
额,为什么会报错呢?
因为虚拟机的参数推断机制特别复杂,我们这样子写虚拟机无法推断出E到底是哪个类。根据我们之前学习的类型推断,虚拟机应该是根据Set extends E> set1,来推断出E的类型,假如?是Integer类型,那么E是什么类型呢。。。只能知道E是Integer的父类而已。所以说,这样子写的话,E其实还是未知的类型,所以会报错。
那么如何解决这个问题呢?
Java提供了显示的类型参数推断机制
Set
告诉该方法,E为Number。非静态方法用this代替Union。
改进计算容器的最大值的方法(calculateMax())
先看一下原先写的代码:
public > T calculateMax(List list){
Iterator iterator = list.iterator();
T result = iterator.next();
while (iterator.hasNext()){
T t = iterator.next();
//需要判断大小
if (result.compareTo(t) < 0){
result = t;
}
}
return result;
}
我们要做什么改进呢?
①、首先输入参数List
②、Comparable
(理解什么是生产者,什么是消费者是至关重要的)
所以最终结果就是:
public > T calculateMax(List extends T> list){
Iterator iterator = list.iterator();//该行报错了
//...以下省略
return result;
}
原因:list.iterator()的泛型是 extends T> 是T的子类 而 被赋值参数的类型是 IteratorIterator extends T> iteraot = list.iterator();