18-1,泛型-概述
1,泛型是JDK1.5出现的新技术,新技术的出现是为了解决问题。
2,泛型可以用于明确一个集合中存储什么类型的元素
ArrayList<String> al = new ArrayList<String>();
al.add("abc");//正确
//al.add(3);//编译直接报错
//在迭代器上加上泛型,下面取出来的时候就不用强转了
Iterator<String> it = al.iterator();
while(it.hasNext()) {
//在这里不用强转了,因为从it中取出的肯定是String
String str = it.next();
System.out.println(str);
}
3,好处
(1)将运行时间的问题ClassCastException转到了编译时期。
(2)避免了强制转换的麻烦。
4,泛型什么时候用呢?
当操作的引用数据类型不确定的时候,就用泛型,将要操作的引用数据类型写在<>中即可。其实<>就是一个用于接收具体引用数据类型的参数范围。
在程序中,只要用到了带有<>的类型或者接口,就要明确传入的具体引用数据类型。
也可以同时接受多个数据类型:
class Tool<e1,e2,e3,e4>
Tool<String,Demo,Person,Integer> t = new Tool<String,Demo,Person,Integer>();
18-2,泛型-擦除&补偿
1,泛型解决了编译时期的安全问题,是给编译器使用的技术。
2,运行时,会将泛型去掉,生成的class文件中是不带有泛型的,这个称为泛型的擦除。
为什么要擦除呢?
因为为了兼容运行时的类加载器,因为泛型是JDK1.5的新特性,如果class文件中加上泛型的话,类加载器会不认识,若想要类加载器认识泛型,类加载器也要升级,为了避免升级类加载器的麻烦,泛型将交由编译器处理,编译通过后就会把他删去,使之前的类加载器也能运行它。
3,泛型的补偿:
擦除后,如果对元素进行取出的赋值动作,如:String str =it.next();是否需要加上强转呢?答案是不用了,首先,加上泛型且编译通过就能保证集合中的数据是指定的类型了,所以认为不会出现规定类型以外的元素。其实,在运行时,是自动添加了一步动作,通过用getClass()方法获取元素的类型再对其进行一次转换动作,就不用使用者在强制转换了。
18-3,泛型-在集合中的应用
1,以TreeSet为例,TreeSet具备比较功能,但存入的类型必须实现Comparable接口并实现了里面的compareTo方法才可以;或者定义一个比较器,实现Comparator接口并实现里面的compare方法也可以。定义TreeSet时,如果不加泛型Eclipse等IDE会有黄色波浪线提示,加上泛型,黄色波浪线即可消失。
2,上一章提到的Person类实现Comparable接口时Comparable接口也需要指定泛型,如:Comparable<Person>,指定后,覆写的compareTo方法中的参数就可以不写Object了,直接写Person就可以,方法体中的代码也不用强制向下转型了。Comparator接口也要加上泛型,它里面的compare方法的两个参数也直接接受Person类型就可以了。例如:
class Person implements Comparable<Person> {
...code...
public int compareTo(Person p) {
//Person p = (Person)p;//强转可以省去,因为定义了泛型
int temp = this.age = p.age;
return temp == 0 ? this.name.compareTo(p.name) : temp;
}
...code...
}
18-4,泛型类
1,以前定义工具类的时候这么定义:
public class Tool {
private Object object;
public Object getObject() {
return object;
}
public void setObject(Object object) {
this.object = object;
}
}
别的类用这个工具类的时候,可以这样做:
Tool tool = new Tool();
/*
这里因为没有Worker这个类,所以是错误的。
Worker为传入的错误值,但编译不会报错,因为Tool中接收的是Object类型,
下面的语句必须做强转才能匹配类型,因为把Worker向上提升为了Object。
*/
tool.setObject(new Worker());
Student stu = (Student)tool.getObject();
工具类是针对所有类型的,而任何一个类都是Object的子类,所以根据多态的特性,通过将参数定义成Object我们就可以使这个方法接收所有类型的参数,在没有泛型的时候我们就只能这么做。
在JDK1.5之后,使用泛型来接收类中要操作的引用数据类型,这就是泛型类。
泛型类什么时候用呢?
当类中的操作的引用数据类型不确定的时候,就是用泛型来表示。
例如:
public class Tool<Q> {
private Q q;
public Q getObect() {
return q;
}
public void setObject(Q object) {
this.q = object;
}
}
有泛型后,在别的类用到工具类的时候,可以这么做:
Tool<Student> tool = new Tool<Student>();
//tool.setObject(new Worker());//编译报错,因为有了泛型指定的Student,所以别的不能进入集合。
tool.setObject(new Student());
Student stu = tool.getObject();
18-5,泛型方法
1,在18-4中有泛型的Tool类中加入泛型方法,Tool<QQ>
public class Tool<Q> {
private Q q;
public Q getObect() {
return q;
}
public void setObject(Q object) {
this.q = object;
}
/*
如果方法不加泛型会报错,因为Tool明确只能接收QQ类型,
所以不能操作W类型,要想让其中一个方法操作W类型,
必须在返回值类型前加上泛型<W>。
*/
public <W> void show(W str) {
System.out.println("show" + str.toString());
}
/*
另外,print接收的是字符串,那么能不能打印字符串的长度呢?
答案是不能的,因为QQ str = &#