转自:http://www.linuxidc.com/Linux/2013-10/90928.htmjava
? 未知类型linux
既然知道List<Cat>并非List<Anilmal>的子类型,那就须要去寻找替他解决的办法, 是AnimalTrianer.act()方法变得更为通用(既能够接受List<Animal>类型,也能够接受List<Cat>等参数)。在java里解决办法就是使用通配符“?”,具体到AnimalTrianer,就是将方法改成act(List<? extends Animal> list),当中“?”就是通配符,而“? extends Animal”则表示通配符“?”的上界为Animal,换句话说就是,“? extends Animal”能够表明Animal或其子类,可表明不了Animal的父类(如Object),由于通配符的上界是Animal。以下,为改进以后的AnimalTrianer数组
public class AnimalTrainer { public void act(List<? extends Animal> list) { for (Animal animal : list) { animal.eat(); } } }
再来测试一下,以下,发现Test 2 能够经过编译了:ide
public class TestAnimal { public static void main(String[] args) { AnimalTrainer animalTrainer = new AnimalTrainer(); //Test 1 List<Animal> animalList = new ArrayList<>(); animalList.add(new Cat("cat1")); animalList.add(new Bird("bird1")); animalTrainer.act(animalList); //能够经过编译 //Test 2 List<Cat> catList = new ArrayList<>(); catList.add(new Cat("cat2")); catList.add(new Cat("cat3")); animalTrainer.act(catList); //也能够经过编译 } }
通过上述分析,能够知道List<Animal>和List<Cat>都是List<? extends Animal>的子类型,相似有List<Bird>,List<Magpie>也是List<? extends Animal>的子类型。测试
现总结以下,对于通配符的上界,有如下几条基本规则:(假设给定的泛型类型为G,(如List<E>中的List),两个具体的泛型参数X、Y,当中Y是X的子类(如上的Animal和Cat))this
学到这里,可能会遇到一些疑惑的地方,或者说事理解不透的地方,先观察以下两段代码片断,判断一下其是否可行??设计
public void testAdd(List<? extends Animal> list){ //....其余逻辑 list.add(new Animal("animal")); list.add(new Bird("bird")); list.add(new Cat("cat")); }
List<? extends Animal> list = new ArrayList<>(); list.add(new Animal("animal")); list.add(new Bird("bird")); list.add(new Cat("cat"));
先分析以下:由于“? extends Animal”可表明Animal或其子类(Bird,Cat),那上面的操做应该是可行的。事实上是”不行“,即没法经过编译。为何呢??htm
在解释以前,再来从新强调一下已经知道的规则:在List<Aimal> list里只能添加Animal类对象及其子类对象(如Cat和Bird对象),在List<Bird>里只能添加Bird类和其子类对象(如Magpie),可不能添加Animal对象(不是Bird的子类),相似的在List<Cat>和List<Magpie>里只能添加Cat和Bird对象(或其子类对象,不过这没有列出)。如今再回头看一下testAdd()方法,咱们知道List<Animal>、List<Cat等都是List<? extends Animal>的子类型。先假设传入的参数为为List<Animal>,则第一段代码的三个“add”操做都是可行的;可若是是List<Bird>呢??则只有第二个“add”能够执行;再假设传入的是List<Tiger>(Tiger是想象出来的,可认为是Cat的子类),则三个“add”操做都不能执行。对象
如今反过来讲,给testAdd传入不一样的参数,三个“add”操做均可能引起类型不兼容问题,而传入的参数是未知的,因此java为了保护其类型一致,禁止向List<? extends Animal>添加任意对象,不过却能够添加null,即list.add(null)是可行的。有了上面谈到的基础,再来理解第二段代码就不难了,由于List<? extends Animal>的类型“? extends Animal”没法肯定,能够是Animal,Bird或者Cat等,因此为了保护其类型的一致性,也是不能往list添加任意对象的,不过却能够添加null。排序
先总结以下:不能往List<? extends Animal> 添加任意对象,除了null。
另外提醒你们注意的一点是,在List<? extends Animal> 能够是Animal类对象或Bird对象等(只是某一类对象),反过来讲,在List<? extends Animal> list里的都是Animal对象,即Bird也是Animal对象,Cat也是Animal对象(用java的语言来讲就是子类能够指向父类,父类却不能指向子类),那么在Animal里的全部方法都是能够调用的,以下:
for (Animal animal : list) { animal.eat(); }
既然有了通配符的上界,天然有着通配符的下界。能够如此定义通配符的下界 List<? super Bird>,其中”Bird“就是通配符的下界。注意:不能同时声明泛型通配符申明上界和下界。
在谈注意细节以前,咱们先看一下通配符的使用规则——对于通配符的上界,有如下几条基本规则:(假设给定的泛型类型为G,(如List<E>中的List),两个具体的泛型参数X、Y,当中Y是X的子类(如上的Animal和Cat))
如今再来看以下代码,判断其是否符合逻辑:
public void testAdd(List<? super Bird> list){ list.add(new Bird("bird")); list.add(new Magpie("magpie")); }
List<? super Bird> list = new ArrayList<>(); list.add(new Bird("bird")); list.add(new Magpie("magpie")); list.add(new Animal("animal"));
看第一段代码,其分析以下,由于”? super Bird”表明了Bird或其父类,而Magpie是Bird的子类,因此上诉代码不可经过编译。而事实上是”行“,为何呢?2?
在解疑以前,再来强调一个知识点,子类能够指向父类,即Bird也是Animal对象。如今考虑传入到testAdd()的全部可能的参数,能够是List<Bird>,List<Animal>,或者List<Objext>等等,发现这些参数的类型是Bird或其父类,那咱们能够这样看,把bird、magpie当作Bird对象,也能够将bird、magpie当作Animal对象,相似的可当作Object对象,最后发现这些添加到List<? supe Bird> list里的对象都是同一类对象(如本文刚开篇提到的Test 1),所以testAdd方法是符合逻辑,能够经过编译的。:
如今再来看一下第二段代码对于,第2、三行代码的解释和上文同样,至于最后一行“list.add(newAnimal("animal"))”是没法经过编译的,为何的??为了保护类型的一致性,由于“? super Bird”能够是Animal,也能够是Object或其余Bird的父类,因没法肯定其类型,也就不能往List<? super Bird>添加Bird的任意父类对象。
既然没法肯定其父类对象,那该如何遍历List<? super Bird> ? 由于Object是全部类的根类,因此能够用Object来遍历。以下,不过貌似其意义不大。
for (Object object : list) {//...}
那“? super BoundingType”能够应用在什么地方呢??“? super BoundingType”应用相对普遍,只不过是混合着用。下面举个简单的例子。先假设有如下两个Student和CollegeStudent,当中CollegeStudent继承Student,以下:
public class Student implements Comparable<Student>{ private int id; public Student(int id) { this.id = id; } @Override public int compareTo(Student o) { return (id > o.id) ? 1 : ((id < o.id) ? -1 : 0); } }
public class CollegeStudent extends Student{ public CollegeStudent(int id) { super(id); } }
先须要根据他们的id对他们进行排序(注意此处是对数组对象进行排序),设计方法以下,(n指数组元素的个数):
public static <T extends Comparable<? super T>> void selectionSort(T[] a,int n)
先理解此方法含义,首先<T extends Comparable<T>>规定了数组中对象必须实现Comparable接口,Comparable<? Super T>表示若是父类实现Comparable接口,其自身可不实现,如CollegeStudent。先假设有一个CollegeStudent的数组,以下:
CollegeStudent[] stu = new CollegeStudent[]{ new CollegeStudent(3),new CollegeStudent(2), new CollegeStudent(5),new CollegeStudent(4)};
执行方法 selectionSort(stu,4)是彻底能够经过的。可若是定义的selectionSort方法以下:
public static <T extends Comparable<T>> void selectionSort(T[] a,int n)
则方法selectionSort(stu,4)不能执行,由于CollegeStudent没有实现Comparable<CollegeStudent>接口。换句话就是“? super T”使selectionSort方法变得更为通用了。selectionSort完整代码的实现可参考本文的末尾。
知道了通配符的上界和下界,其实也等同于知道了无界通配符,不加任何修饰便可,单独一个“?”。如List<?>,“?”能够表明任意类型,“任意”也就是未知类型。无界通配符一般会用在下面两种状况:
一、当方法是使用原始的Object类型做为参数时,以下:
public static void printList(List<Object> list) { for (Object elem : list) System.out.println(elem + ""); System.out.println(); }
能够选择改成以下实现:
public static void printList(List<?> list) { for (Object elem: list) System.out.print(elem + ""); System.out.println(); }
这样就能够兼容更多的输出,而不单纯是List<Object>,以下:
List<Integer> li = Arrays.asList(1, 2, 3); List<String> ls = Arrays.asList("one", "two", "three"); printList(li); printList(ls);
二、在定义的方法体的业务逻辑与泛型类型无关,如List.size,List.cleat。实际上,最经常使用的就是Class<?>,由于Class<T>并无依赖于T。
最后提醒一下的就是,List<Object>与List<?>并不等同,List<Object>是List<?>的子类。还有不能往List<?> list里添加任意对象,除了null。
附录:selectionSort的代码实现:(若是须要实现比较好的输出,最好重写Student的toString方法)
public class SortArray { //对一组数组对象运用插入排序,n指数组元素的个数 public static <T extends Comparable<? super T>> void selectionSort(T[] a,int n) { for (int index = 0; index < n-1; index++) { int indexOfSmallest = getIndexOfSmallest(a,index,n-1); swap(a,index,indexOfSmallest); } } public static <T extends Comparable<? super T>> int getIndexOfSmallest(T[] a, int first, int last) { T minValue = a[first]; // 假设第一个为minValue int indexOfMin = first; // 取得minValue的下标 for (int index = first + 1; index <= last; index++) { if (a[index].compareTo(minValue) < 0) { minValue = a[index]; indexOfMin = index; } } return indexOfMin; } public static void swap(Object[] a,int first,int second) { Object temp = a[first]; a[first] = a[second]; a[second] = temp; } public static void main(String[] args) { CollegeStudent[] stu = new CollegeStudent[]{ new CollegeStudent(3), new CollegeStudent(2), new CollegeStudent(5), new CollegeStudent(4)}; selectionSort(stu, 4); for (Student student : stu) { System.out.println(student); } } }