概述:
集合中是可以存放任意对象的,只要把对象丢进集合后,那么这时他们都会被提升成Object类型。当我们在取出每一个对象,并且进行相应的操作,这时必须采用类型转换。
举例:
public static void main ( String[] args ) {
ArrayList list = new ArrayList();
list.add("abc");
list.add("itcast");
list.add(5); //由于集合没有做任何限定,任何类型都可以给其中存放
Iterator it = list.iterator();
while (it.hasNext()) { //需要打印每个字符串的长度,就要把迭代出来的对象转成String类型
String str = (String) it.next();
System.out.println(str);
}
}
}
则会出现以下情况:
abc
itcast
Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
at Day13.Demo02.main(Demo02.java:22)
Process finished with exit code 1
程序在运行时发生了java.lang.ClassCastException
类型转化异常:
由于集合中什么类型的元素都可以存储。导致取出时,如果出现强转就会引发运行时ClassCastException异常。
我们知道在前面学习数组时,只要数组定义好,那么这时给数组中存放的数据类型就确定,这时如果给数组中存放的数据类型和数组类型不一致,编译时就会报错。
JDK1.5以后,出现了解决方案,使用容器时,必须明确容器中元素的类型。这种机制称之为 :泛型。
泛型格式:
<数据类型>,这种格式不是很难理解,<>尖括号也是括号,往括号里面写东西其实就是在传递参数。
泛型这种机制的好处:
1,安全机制。
2,将运行时期的ClassCastException,转移到了编译时期变成了编译失败。
3,泛型技术,是给编译器使用的技术。
4,避免了强转的麻烦。
public static void main ( String[] args ) {
ArrayList<String> list = new ArrayList<String>();
list.add("abc");
list.add("itcast");
list.add("5"); //由于集合没有做任何限定,任何类型都可以给其中存放
Iterator<String> it = list.iterator();
while (it.hasNext()) { //需要打印每个字符串的长度,就要把迭代出来的对象转成String类型
String str = it.next();
System.out.println(str);
}
}
}
结果如下:
abc
itcast
5
Process finished with exit code 0
泛型通配符
无法确定具体集合中的元素类型是什么,就可以使用泛型的通配符机制来完成。
当使用泛型类或者接口时,传递的具体的类型不确定,可以通过通配符<?>表示。但是一旦使用泛型的通配符机制后,只能使用Object类中的共性方法,集合中元素自身方法无法使用。通常这样来写:List<?>list。
泛型限定
想要对被打印的集合中的元素类型进行限制,只在指定的一些类型,进行打印。怎么做呢?要解决这个问题,我们就要学习泛型限定
对泛型界限的理解
我们定义一个Box,现在我们想象里面可以装水果那我们就可以这样写
Box<Fruit> box=Box<Apple>(new Apple)
不过这样的写是不行的,编译器会报错所以就会有
限定泛型的上限:? extends E:接收E类型或者E的子类型。当然有上限,
肯定也有泛型的下限:? super E:接收E类型或者E的父类型。
现在我们把上面的 Box 定义改成:
Box<? extends Fruit>
这就是上界通配符, 这样 Box 及它的子类如 Box 就可以赋值了。
Box<? extends Fruit> box = new Box<Apple>(new Apple)
其中蓝色代表Box<? extends Fruit> box
上界代表只能取树的叶子,只能取外围不能往里放
<? super Fruit>上界通配符对应黄色区域
表示想要放这个水果必须有盒子能实现水果的基类,也就是意思说要实现多态。能放食物的盒子可以去放水果,放水果的盒子重写了食物的放的内容。
也就是所在叶子的必须有一个根,叶子就是你想去实现的内容。
使用场景
在使用泛型的时候可以遵循一些基本的原则,从而避免一些常见的问题。
在代码中避免泛型类和原始类型的混用。比如List和List不应该共同使用。这样会产生一些编译器警告和潜在的运行时异常。当需要利用JDK 5之前开发的遗留代码,而不得不这么做时,也尽可能的隔离相关的代码。
在使用带通配符的泛型类的时候,需要明确通配符所代表的一组类型的概念。由于具体的类型是未知的,很多操作是不允许的。
泛型类最好不要同数组一块使用。你只能创建new List<?>[10]这样的数组,无法创建new List[10]这样的。这限制了数组的使用能力,而且会带来很多费解的问题。因此,当需要类似数组的功能时候,使用集合类即可。
不要忽视编译器给出的警告信息。
PECS 原则
如果要从集合中读取类型T的数据, 并且不能写入,可以使用 上界通配符(<?extends>)—Producer Extends。
如果要从集合中写入类型T 的数据, 并且不需要读取,可以使用下界通配符(<? super>)—Consumer Super。
如果既要存又要取, 那么就要使用任何通配符。