本篇文章我们来介绍下泛型相关的知识。
1. 使用泛型的好处
泛型是JDK1.5出现的安全机制。
使用泛型的好处:
- 将运行时期的问题ClassCastException转移到了编译时期。
- 避免了强制类型转换的麻烦。
泛型技术是给编译器使用的技术,用于编译时期,确保了类型的安全。
2. 泛型擦除和泛型补偿
代码运行时,会将.java文件中的泛型去掉,生成的classA文件是不带泛型的。
为什么擦除泛型呢?
因为为了兼容运行时的类加载器。
取出元素的时候不加强转了,那你还知道取出的类型是啥类型吗?泛型补偿
在以前的类加载器上写一个反省补偿程序。
在运行时,通过元素的类型进行转换动作,使用者不用专门在做强制类型转换了。
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("abc");
list.add("haha");
Iterator<String> it = list.iterator();
while (it.hasNext()) {
String string = (String) it.next(); // 有强转
System.out.println(string);
}
}
运行结果:

将上面的代码中while循环体中(String) it.next()的强制类型转换去掉,代码也能正常运行,就是因为有泛型补偿机制。

3. 泛型类/泛型接口/泛型方法
泛型的其他作用:
提供设计上的便捷,提高代码复用性。
- 自定义泛型类:在类名称后面定义泛型ABC,在类内容中使用泛型ABC
- 自定义泛型方法:在方法的返回值类型前面定义泛型W,在方法参数列表中使用泛型W
- 自定义泛型接口:将泛型定义在接口上。
3.1 泛型类
泛型类:在类名后加泛型的类叫泛型类。
泛型类什么时候用?
当类中操作的引用数据类型不确定的时候,使用泛型来表示。在jdk1.5后使用泛型来接收类中要操作的引用数据类型。
我们想写一个工具,工具在setXxx和getXxx方法中需要传入的类型不确定,那么我们可以将参数类型设为Object,这样就既可以传入Student类型,也可以传入Person类型。
但是,在运行时候发现,很容易出现如下类型转换异常。如下图:
package com.yansong.p1.generic;
import com.yansong.bean.Person;
import com.yansong.bean.Student;
public class GenericDemo01 {
public static void main(String[] args) {
Tool tool = new Tool();
tool.setObj(new Person());
Student stu = (Student)tool.getObj();
}
}
package com.yansong.p1.generic;
public class Tool {
private Object obj;
public Object getObj() {
return obj;
}
public void setObj(Object obj) {
this.obj = obj;
}
}

引入泛型类
在jdk1.5后,使用泛型来接收类中要操作的引用数据类型。
泛型类什么时候用呢?
当类中的操作的引用数据类型不确定的时候,就使用泛型来表示。
泛型类是在类名称后面加泛型的类。比如下面代码中的ABC,由于引用数据类型不确定,我们就可以使用泛型来解决,在使用的时候传入Person对象/Student对象/Worker对象都看可以。
public class ToolWithGeneric<ABC> //在类类名称后面定义泛型
用法:
package com.yansong.p1.generic;
public class ToolWithGeneric<ABC> { //在类名称后面定义泛型ABC
private ABC object; // 在类内容中使用泛型ABC
public ABC getObject() {
return object;
}
public void setObject(ABC object) {
this.object = object;
}
}
使用自定义泛型类:
- 重复利用。ToolWithGeneric这个类使用了泛型,没有指定具体操作的数据类型。在使用的时候将ABC才根据需要确定成Person类或Student类。
- 提高安全性。下图中的代码,当18行代码中将泛型指定成Student类型的时候,在19行给steXXX的时候传入Person就会报错。
- 在13行代码中指定泛型为Student,15行获取对象的时候也不用专门在加强制类型转换了。

3.2 泛型方法
将泛型定义在方法上。
定义位置在方法返回值的前边,修饰字符的后边。
另外:
- 当方法静态时,不能访问类上定义的泛型,只能将泛型定义在方法上。
- 一旦使用了泛型,就无法使用某个类特有的方法了,只能用Object的方法,为什那么呢?因为泛型真正在使用的时候时候是哪个类不确定啊,所以就不能使用特有的方法,而Object类的方法是所有类都有的方法,所以可以使用。
// 将泛型定义在方法上
public <W> void show(W str){
System.out.println("show : "+str.toString());
}
public static <Y> void method(Y obj){
System.out.println("method:"+obj);
}
具体演示: 我现在想创建一个show方法,它可以对任何对象进行显示,应该怎么做呢? 分析一下,其实就是方法参数类型不确定。
tips:当然了,不使用泛型的话我们可以将参数类型定为Object。但是这们讲的是泛型。
package com.yansong.p1.generic;
public class ToolWithGeneric<ABC> {
private ABC object;
public ABC getObject() {
return object;
}
public void setObject(ABC object) {
this.object = object;
}
//需要show的类型不确定,
//我们自定义个一个泛型W,并在show方法上使用这个泛型,
//具体使用的时候W可以是 double,String,Person,Object
public <W> void show(W w) {
System.out.println("show : "+w.toString());
}
}
package com.yansong.p1.generic;
import hashdemo.Person;
public class GenericDemo2 {
public static void main(String[] args) {
ToolWithGeneric<String> toolWithGeneric = new ToolWithGeneric<>();
toolWithGeneric.show(new Integer(100));
toolWithGeneric.show("abc");
toolWithGeneric.show(3.14159);
toolWithGeneric.show(new Person("yansong", "男"));
}
}
运行结果:

3.3 泛型接口
将泛型定义在接口上。
package com.yansong.p1.generic;
import com.yansong.bean.Student;
public class GenericDemo3 {
public static void main(String[] args) {
StrInterfaceImpl strImpl = new StrInterfaceImpl();
strImpl.show("abc");
StudentInterfaceImpl stuImpl = new StudentInterfaceImpl();
stuImpl.show(new Student("霍建华", "男", 115, "研究生2年级"));
}
}
//泛型接口,将泛型定义在接口上。
interface Interface<T>{
public void show(T t);
}
//实现类在具体使用的时候将泛型明确,比如这里使用的String
class StrInterfaceImpl implements Interface<String>{
public void show(String str){
System.out.println("show :"+str);
}
}
//实现类在具体使用的时候将泛型明确,比如这里使用的String
class StudentInterfaceImpl implements Interface<Student>{
public void show(Student stu){
System.out.println("show :"+stu.toString());
}
}
运行结果:
