概念
泛型是JDk1.5版本以后出现的新特性,用于解决安全问题,是一个类型安全机制
问题的引出:在jdk1.5之前我们是这样编写代码的
public class GenericDemo { public static void main(String[] args) { ArrayList al=new ArrayList(); al.add("abc"); al.add("abcf"); al.add("fsadsa"); al.add(2); for(Iterator it=al.iterator();it.hasNext();){ String s=(String)it.next(); System.out.println(s); } } }
上面程序在编译时候不会出错,而运行时期会出现类型转换异常ClassCastException,为了解决这一问题,引入泛型。上面代码可修改为
ArrayList<String> al=new ArrayList<String>(); al.add("abc"); al.add("abcf"); al.add("fsadsa"); // al.add(2);//此处编译不能通过 for(Iterator<String> it=al.iterator();it.hasNext();){ String s=it.next(); System.out.println(s); }
好处:
1、将运行时期出现的ClassCastException,转移到了编译时期。方便程序员解决问题。让运行时问题减少,安全
2、避免了强制转换麻烦
格式:通过<>来定义要操作的引用数据类型
注意:泛型是提供给JAVA编译器使用的,可以限定集合中的输入类型,让编译器挡住源程序中的非法输入,编译器编译带类型说明的集合时会去除掉“类型”信息,似程序运行效率不受影响,对于参数化的泛型类型,使用getClass()的返回值和原始类型完全一样,由于编译器生成的字节码文件会自动去掉泛型信息,只要跳过编译器,就可以往某个指定类型的集合中存入其他数据类型。例如,用反射得到集合,再用add方法加入即可。代码如下
al.getClass().getMethod("add", Object.class).invoke(al, 1); System.out.println(al);
打印结果为[abc, abcf, fsadsa, 1]发现通过反射技术可以越过编译器给集合赋其他类型的值,当然遍历的时候如果指定类型依然会有异常
泛型类
当类中要求操作的引用数据类型不确定的时候,早期定义Object来完成扩展,现在定义泛型来完成扩展
代码示例
class Worker { } class Student { } //早期代码 /* class Tool { private Object obj; public void setObject(Object obj) { this.obj = obj; } public Object getObject() { return obj; } } */ //引入泛型机制后的代码 class Tool<T> { private T t; public void setObject(T t) { this.t = t; } public Object getObject() { return t; } } public class GenericDemo3 { public static void main(String[] args) { //早期代码 // Tool t=new Tool(); // t.setObject(new Worker());//此步骤如果传入new Student()编译不会报错,运行会出错,需要开发者主观指定参数类型 // Worker w=(Worker)t.getObject(); //引入泛型机制后的代码 Tool<Student> t=new Tool<Student>(); t.setObject(new Student()); } }
泛型方法
泛型类定义的类型在整个类中有效,如果被方法使用,那么泛型类的对象明确要操作的类型后所要操作的类型就已经固定了,为了让不同的方法可以操作不同的类型而且类型还不确定那么可以将泛型定义在方法上
class Demo{ public <T> void show(T t) { System.out.println("show:" + t); } public <T> void print(T t) { System.out.println("print:" + t); } } public class GenericDemo4 { public static void main(String[] args) { Demo d=new Demo(); d.show("aaaa"); d.print(3); d.print("dddd"); Demo.method("static"); } }
注意:如果类中和方法中都定义了泛型,这样是不重复的,定义了泛型的方法不会受类中泛型类型的限制
静态泛型方法
静态方法不可以访问类上定义的泛型,因为静态成员是随着类的加载而加载的,这时候还不知道类指定的泛型类型,如果静态方法定义的引用数据类型不确定,可以将泛型定义在方法上,将泛型写在方法返回值类型前面,
public static <T> void method(T t){ System.out.println("method:"+t); }
泛型接口
//泛型定义在接口上。 interface Inter<T> { void show(T t); } /* class InterImpl implements Inter<String> { public void show(String t) { System.out.println("show :"+t); } } */ class InterImpl<T> implements Inter<T> { public void show(T t) { System.out.println("show :"+t); } } class GenericDemo5 { public static void main(String[] args) { InterImpl<Integer> i = new InterImpl<Integer>(); i.show(4); //InterImpl i = new InterImpl(); //i.show("haha"); } }
泛型限定
通配符”?“
写一个方法可以遍历任意类型的ArrayList数组,集合中泛型的类型不确定,使用?通配符占位
public static void printColl(ArrayList<?> al) { Iterator<?> it = al.iterator(); while (it.hasNext()) { System.out.println(it.next()); } }
当然,这里也可以用泛型方法实现
public static <T> void printColl(ArrayList<T> al) { Iterator<T> it = al.iterator(); while (it.hasNext()) { T t=it.next(); System.out.println(t); } }
那么,?和T有什么区别呢?使用T,代表一个具体类型,可以对T进行操作,?仅代表占位符
泛型限定
? extends E:可以接受E类型或者E类型的子类型
? super E:可以接受E类型或者E类型的父类型
之前设置泛型类型的时候,实际上都是可以任意设置的,只要是类就可以设置,但是在java的泛型中可以指定一个泛型的上限和下限在引用传递中,泛型操作中也可以设置一个泛型对象的范围上限和范围下限。范围上限使用extends关键字声明,表示参数化的类型可能是所指定的类型,或者是此类型的子类,而范围下限使用super进行声明,表示参数化的类型可能是所指定的类型,或者是此类型的父类型,直至Object类
先写三个类Person、两个子类Student、Workers
class Person{ private String name; Person(String name){ this.name=name; } public String getName(){ return name; } } class Students extends Person{ Students(String name) { super(name); } } class Workers extends Person{ Workers(String name){ super(name); } }
设置上限
写一个遍历集合的方法,可以遍历Person类以及子类
public static void printColl(ArrayList<? extends Person> al){ Iterator<? extends Person> it=al.iterator(); while (it.hasNext()) { System.out.println(it.next().getName()); } }
设置下限
写一个遍历集合的方法,可以遍历Workers类以及父类
public static void printTree(TreeSet<? super Workers> ts){ Iterator< ?super Workers> it=ts.iterator(); while (it.hasNext()) { System.out.println(it.next()); } }