普通数组的工作方式
如下面的代码,函数takeAnimals
的参数类型是Animal[[]
,在调用的时候,可以把Animal[]
或者Dog[]
传递进去( Dog是Animal的派生类)
import java.util.*;
public class TestGenerics1 {
public static void main( String[] args ) {
new TestGenerics1().go();
}
public void go() {
Animal[] animals = { new Dog(), new Cat(), new Dog() };
Dog[] dogs = { new Dog(), new Dog(), new Dog() };
takeAnimals( animals );
takeAnimals( dogs );
}
public void takeAnimals( Animal[] animals ) {
for( Animal a : animals ) {
a.eat();
}
}
}
泛型的工作方式
我们把上例的Array替换为ArrayList
public void go() {
ArrayList<Animal> animals = new ArrayList<Animal>();
animals.add( new Dog() );
animals.add( new Cat() );
animals.add( new Dog() );
takeAnimals( animals );
}
public void takeAnimals( ArrayList<Animal> animals ) {
for( Animal a : animals ) {
a.eat();
}
}
如上,takeAnimals
传入ArrayList<Animal>
是可以正常编译运行,没有问题的;但是如果仿照Array的例子,我们可以将ArrayList<Dog>
传入takeAnimals
吗?
ArrayList<Dog> dogs = new ArrayList<Dog>();
dogs.add( new Dog() );
dogs.add( new Dog() );
dogs.add( new Dog() );
takeAnimals( dogs );
上面这段code会compile error。
如果可以会怎样?
public void takeAnimals( ArrayList<Animal> animals ) {
animals.add( new Cat() );
}
如果ArrayList<Dog>
可以传递给takeAnimals
,那会出现Cat混入Dog集合的情形。
原因
数组的类型是在运行期间检查的,但集合的类型检查只会发生在编译期间
解决办法
public void takeAnimals( ArrayList<? extends Animal> animals ) {
for( Animal a: animals ) {
a.eat();
}
}
在使用带有<?>
的声明时,编译器不会让你往集合中加入任何东西,只能读取。
另外一种相同功能的语法
public <T extends Animal> void takeAnimals( ArrayList<T> animals );
为什么要用这种形式?
如果要多次使用,会更有效率
public <T extends Animal> void takeThing< ArrayList<T> one, ArrayList<T> two )
// 而不必像下面这样
public void takeThing< ArrayList<? extends Animal> one, ArrayList<? extends Animal> two )