技术开发 频道

java泛型笔记(1)

  【IT168 文档】本来jdk5之前有java.util.List这个Interface是这么用的:

List myIntList = new LinkedList(); // LinkedList是实现了List Interface的类

myIntList.add(
new Integer(0));

Integer x
= (Integer)myIntList.iterator().next();

  这里myIntList其实可以myIntList.add(new Object());因为声明语句就没有写出该List到底支持什么。所以myIntList.iterator().next()返回的只能是Object,它并不知道这里面装的到底是什么具体的类。

  因为这个List里既可能有Integer对象,也可能有Double对象,为了不要(Integer)这个强制转换,我们有了这个:

List<Integer> myIntList = new LinkedList<Integer>();

   这个时候,Integer x = (Integer)myIntList.iterator().next();就可以去掉强制转换写成

Integer x = myIntList.iterator.next();

   因为编译器确实知道了myIntList里放的就是Integer,不会是其他。这个地方要注意:List myIntList = new LinkedList(); 这句话的真正含义右边是创建一个LinkedList的对象,然后把引用给左边一个叫myIntList的名字。理解下面两个例子就知道了:

List myIntList = new LinkedList<Integer>(); //警告,不出错 myIntList可以add(Object)

List
<Integer> myIntList = new LinkedList(); //警告,不出错 myIntList只能add(Integer)

List
<Integer> myIntList = new LinkedList<Double>(); // 出错

  事情到这里,看似所有问题都解决了,但是不尽然。慢慢道来:

class Shape {

public void say();

};

class Circle extends {

public void say() {

System.out.println(
"i am a circle");

}

};

  通过上面,我们可以

Shape shape = new Circle();

shape.say();
// i am a circle.

   这里表明,父类可以代表子类。但是,List却不是List的父类,如果有个函数:

public void sayAll(List<Shape> list) {

                 for(Shape s : list) {

                             s.say();

                               }
                  }

   那么这个函数不能被这样调用:

List<Circle> circleList = new List<Circle>();

sayAll(circleList);

   只能被这样调用:

List<Shape> shapeList = new List<Shape>();

sayAll(shapeList);

  ##这里详细解释为什么List不能代表List,看下面代码就知道:

List<Circle> circleList = new List<Circle>();

List
<Shape> shapeList = circleList;

shapeList.add(
new Shape());

circleList.iterator().next();
//出错。

   那么怎么解决这个问题?可以这样解决:

public void sayAll(List<?> list) {

                  for(Object s : list) {

                  }

       }

   具体我们刚才讨论的Shape和Circle的问题可以这样写:

public void sayAll(List<? extends Shape> list) {

          
for(Shape s : list) {

              s.say();

          }

}

   其中public void sayAll(List list) 就是public void sayAll(List list) 的意思,这个例子说明,List才是List和List的“父类”,可以传递引用。也说明List是List的“父类”,可以传递引用,而不是List的“父类”,因为Integer extends Shape不成立,我们再来看一个例子来加深印象,结束这个话题。然后讨论新话题。

class Person {}

class Student extends Person{}

public class Census {

public static void addRegistry(Map<String, ? extends Person> registry) { ... }

};

Map
<String,Driver> allDrivers = ...

Census.add(allDrivers);

  我们下一个话题,先由这个引起:我们说过,如果List是List的“父类”,那么会出现一个矛盾:

List<Circle> circleList = new List<Circle>();

List
<Shape> shapeList = circleList; //错误

shapeList.add(
new Shape());

circleList.iterator().next();
//出错。

  那么List确实是List的“父类”,会有这个矛盾不?同样有,如下:

List<Circle> circleList = new List<Circle>();

List
<?> list= circleList; //正确

list.add(
new Shape()); //错误

  为什么list不能加新元素?因为list里的元素类型已经定义好了,是?,不是任何类型,所以不能加入任何类型的元素。

  所以如下方法是不能使用的:

static void fromArrayToCollection(Object[] a, Collection<?> c) {

          
for (Object o : a) {

      c.add(o);
// compile time error 因为c不能加入元素

          }

}

  但是我们想要在List里加入元素如何弄呢?一个例子概括如下:

static <T> void fromArrayToCollection(T[] a, Collection<T> c) {

      
for (T o : a) {

c.add(o);
// correct

      }

}

  那么这样,c这个Collection就能加入东西了,加入的东西必须是T类,看例子:

Object[] oa = new Object[100];

Collection
<Object> co = new ArrayList<Object>();

fromArrayToCollection(oa, co);
// T inferred to be Object



String[] sa
= new String[100];

Collection
<String> cs = new ArrayList<String>();

fromArrayToCollection(sa, cs);
// T inferred to be String

fromArrayToCollection(sa, co);
// T inferred to be Object



Integer[] ia
= new Integer[100];

Float[] fa
= new Float[100];

Number[] na
= new Number[100];

Collection
<Number> cn = new ArrayList<Number>();

fromArrayToCollection(ia, cn);
// T inferred to be Number

fromArrayToCollection(fa, cn);
// T inferred to be Number

fromArrayToCollection(na, cn);
// T inferred to be Number

fromArrayToCollection(na, co);
// T inferred to be Object

fromArrayToCollection(na, cs);
// compile-time error

   到现在为止,上面一共讲了两个jdk1.5中新的事物

List<? extends Parent> list = new LinkedList<Child>(); // wildcards

static <T> void fromArrayToCollection(T[] a, Collection<T> c)

   那么看下面两个例子,他们做的事情都是一样的,但是分别用了这两个方法:

interface Collection<E> {

public boolean containsAll(Collection<?> c);

public boolean addAll(Collection<? extends E) c);        

}

interface Collection<E> {

public <T> boolean containsAll(Collection<T> c);

public <T extends E> boolean addAll(Collection<T> c);

}

   他们都做两件事:判断任意类的容器是不是在本身这容器里。加入E子类的容器到本身的容器里。

  也可以同时都用上两个新东西:

class Collection {

public static <T> void copy(List<T> dest, List<? extends T> src){}

}

   或者写成

class Collection {

public static <T,S extends T> void copy(List<T> dest, List<S> src){}

}

   作为总结这两个新的java特性,举出一个复杂例子:

static List<List<? extends Shape>> history = new ArrayList<List<? extends Shape>>();

public void drawAll(List<? extends Shape> shapes) {

history.addLast(shapes);

for(Shape s : shape) {

s.draw(
this);

}

}
0
相关文章