1.概述
在前面学习集合时,我们知道了集合中是可以存放任意对象的,只要把对象存储到集合后,那么这时他们都会被提升成Object类型。当我们在取出每一个对象,并且进行相应的操作时,必须采用类型转换。比如下面程序:
public static void main(String[] args) {
List list = new ArrayList();
list.add("abc");
list.add("def");
list.add(123);// 由于集合没有做任何限定,任何类型都可以给其中存放
// 相当于:Object obj=new Integer(5);
Iterator it = list.iterator();
while (it.hasNext()) {
// 需要打印每个字符串的长度,就要把迭代出来的对象转成String类型
String str = (String) it.next();// String str=(String)obj;
// 编译时期仅检查语法错误,String是Object的儿子可以向下转型
// 运行时期String str=(String)(new Integer(5)),
// 由于String与Integer没有父子关系所以转换失败。
// 程序在运行时发生了问题java.lang.ClassCastException
System.out.println(str.length());
}
}
可以发现:这种数据类型间的强制转换,存在安全隐患,所以为了解决这个安全问题,Java中引入了泛型技术。
2.定义
泛型就是指明集合中存储数据的类型。如果没有指明数据类型,那集合可以存储任意类型,就会存在上述提到的类型转换异常的安全隐患。
3.使用
在集合类型后面写上尖括号<>,并加上要存储的数据类型。如:ArrayList
示例:
public static void function() {
Collection<String> coll = new ArrayList<String>();//指明集合中存储的数据类型为String
coll.add("abc");
coll.add("qwe");
//coll.add(123);
Iterator<String> it = coll.iterator();//集合指明为String,所以迭代器取出来的元素也必须为String
while (it.hasNext()) {
String s = it.next();//此处便不需要数据类型强转了
System.out.println(s.length());
}
}
使用泛型后,不用做数据类型的转换,一定程度上精简了代码;并且如果添加的元素不符合所指明的数据类型,代码将会标红报错,无法编译,提高了安全性。
4.补充
java中的泛型,其实是一种伪泛型,它只是一种编译手段,如:ArrayList
如果add中的对象是String则编译成功,否则编译失败。但是:在编译后的class文件中,是没有泛型的。所以java中的泛型是假的,编译后的class文件中没有泛型概念。也就是说如果Java文件反编译回源文件是没有泛型的。(好像C++中的泛型是真的 ^ o ^ )
5.带有泛型的类、方法和接口
上文中我们在集合中大量使用到了泛型,但泛型同样可灵活地将数据类型应用到不同的类、方法、接口中。将数据类型作为参数进行传递。
泛型的类:语法修饰符 class 类名
。E:Element 元素,实际思想就是一个变量而已。如:ArrayList
,E接受到的类型就是Integer类型。
泛型的方法:语法修饰符
。例如,API中的ArrayList集合中的方法:public
。
泛型的接口:语法修饰符 interface 接口名
。如,public interface List
总之在使用时直接将E视为一个未知或任意的数据类型就行了,你传的什么类型,E就变为那个类型。
6.泛型的通配符
直接看示例代码:虽然还没讲到HashSet,但其公共用法都差不多,这里也不需要纠结于其用法,只是为了代码演示。
public class GenericDemo {
public static void main(String[] args) {
ArrayList<String> array = new ArrayList<String>();//存储String类型
HashSet<Integer> set = new HashSet<Integer>();//存储Integer类型
array.add("123");
array.add("456");
set.add(789);
set.add(890);
/*
* 这时需要定义一个方法,要求可以同时迭代这2个集合,怎么办呢?
* 这时会发现 , 数据类型不确定,可能是String的,也可能是Integer的,参数既不能写ArrayList,也不能写HashSet。
* 那么有没有一种共同实现的接口呢?
* 这里需要用到泛型的 通配 ,作用是:匹配所有的数据类型。符号是: ?
*/
iterator(array);
iterator(set);
}
public static void iterator(Collection<?> coll){
Iterator<?> it = coll.iterator();
while(it.hasNext()){
//it.next()获取的对象,任意类型。
System.out.println(it.next());
}
}
}
7.泛型的限定
同样直接看示例代码:(要求定义一个方法,可以同时遍历3集合,遍历三个集合的同时,还可以调用对象的方法)
酒店的员工类:
/*
* 酒店的员工类
* 员工共性, 姓名,工号 工作方法
*/
public abstract class Employee {
private String name;
private String id;
public Employee(){}
public Employee(String name,String id){
this.name = name;
this.id = id;
}
public abstract void work();
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}
VIP接口:
/*
* 酒店的VIP服务
* 厨师和服务员
*/
public interface VIP {
public abstract void services();
}
厨师类:
/*
* 定义厨师类
* 属于员工一种,具有额外服务功能
* 继承Employee,实现VIP接口
*/
public class ChuShi extends Employee implements VIP{
//空参数构造方法
public ChuShi(){}
//有参数构造方法
public ChuShi(String name,String id){
super(name,id);
}
//抽象方法重写
public void work(){
System.out.println("厨师在炒菜");
}
public void services(){
System.out.println("厨师做菜加量");
}
}
服务员类:
/*
* 定义服务员类
* 属于员工一种,具有额外服务功能
* 继承Employee,实现VIP接口
*/
public class FuWuYuan extends Employee implements VIP{
//空参数构造方法
public FuWuYuan() {
super();
}
//满参数构造方法
public FuWuYuan(String name, String id) {
super(name, id);
}
//抽象方法重写
public void work(){
System.out.println("服务员在上菜");
}
public void services(){
System.out.println("服务员给顾客倒酒");
}
}
经理类:
/*
* 定义经理类
* 属于员工一种,没有VIP功能
* 自己有奖金属性
*/
public class JingLi extends Employee {
//空参数构造方法
public JingLi(){}
//满参数构造方法
public JingLi(String name,String id,double money){
super(name, id);
this.money = money;
}
//定义奖金属性
private double money;
public double getMoney() {
return money;
}
public void setMoney(double money) {
this.money = money;
}
//重写抽象方法
public void work(){
System.out.println("管理,谁出错我罚谁");
}
}
运行主程序类:
/*
* 将的酒店员工,厨师,服务员,经理,分别存储到3个集合中
* 定义方法,可以同时遍历3集合,遍历三个集合的同时,可以调用工作方法
*/
import java.util.ArrayList;
import java.util.Iterator;
public class GenericTest {
public static void main(String[] args) {
//创建3个集合对象
ArrayList<ChuShi> cs = new ArrayList<ChuShi>();
ArrayList<FuWuYuan> fwy = new ArrayList<FuWuYuan>();
ArrayList<JingLi> jl = new ArrayList<JingLi>();
//每个集合存储自己的元素
cs.add(new ChuShi("张三", "后厨001"));
cs.add(new ChuShi("李四", "后厨002"));
fwy.add(new FuWuYuan("翠花", "服务部001"));
fwy.add(new FuWuYuan("酸菜", "服务部002"));
jl.add(new JingLi("小名", "董事会001", 123456789.32));
jl.add(new JingLi("小强", "董事会002", 123456789.33));
/*
* 要求定义方法,可以同时遍历3集合,遍历三个集合的同时,可以调用工作方法 work
* ? 通配符,迭代器it.next()方法取出来的是Object类型,无法调用work方法
* 方法一:强制转换: it.next()=Object o ==> Employee
* 方法二:参数控制,可以传递Employee对象,也可以传递Employee的子类的对象,
* 这就要用到泛型的限定 本案例中,父类固定Employee,但是子类可以为任意。
* 语法为: < ? extends Employee > 限制的是父类, 上限限定, 可以传递Employee,也可以传递它的子类对象。
*/
iterator(jl);
iterator(fwy);
iterator(cs);
}
public static void iterator(ArrayList<? extends Employee> array){
Iterator<? extends Employee> it = array.iterator();
while(it.hasNext()){
//获取出的next() 数据类型,可以是任意Employee类型
Employee e = it.next();
e.work();
}
}
}
另外,< ? super Employee >
限制的是子类, 下限限定, 可以传递Employee和它的父类对象。注意与< ? extends Employee >
区别。