一、Java泛型入门基础
1、 泛型历史:集合中可以存储任意类型对象,但是在取出时,如果要使用具体对象的特有方法时,需要进行向下转型,如果存储的对象类型不一致,在转型过程中就会出现ClassCastException异常。这样就给程序带来了不安全性。
在jdk1.5以后就有了解决方案——泛型技术:在存储元素时,就不允许存储不同类型的元素。存储了就编译失败。 所以就需要在存储元素时,在容器上明确具体的元素类型,这其实和数组定义很像。
2、优势:1)将运行时期的ClassCastException异常转移到了编译时期,进行检查,并以编译失败来体现。 这样有利于程序员尽早解决问题。
2)避免了向下转型(强转)的麻烦。
3、在什么情况下使用泛型呢?
只要在使用类或者接口时,该类或者接口在api文当描述时都带着<>,就需要在使用时定义泛型。
其实,泛型无非就是通过<>定义了一个形式参数,专门用于接收具体的引用类型。在使用时,一定要传递对应的实际参数类型。
集合中泛型的应用特别多见。
二、Java泛型使用
使用泛型的动机举例(以集合为例):
class MySet:(自行写的MySet,实现的Java泛型)
public class MySet<E> { private Object[] objs=new Object[0]; public boolean add(E obj){ if(contains(obj)){ return false; } Object tempObjs[] = new Object[objs.length+1]; System.arraycopy(objs, 0, tempObjs, 0, objs.length); tempObjs[objs.length] = obj; objs = tempObjs; return true; } public boolean contains(E obj){ for(Object o:objs){ if(o.equals(obj)){ return true; } } return false; } public Object[] getAll(){ return objs; } public int size(){ return objs.length; } }
测试class:
import java.util.ArrayList; import java.util.Iterator; public class GenericDemo2 { public static void main(String[] args) { // MySet<String> set = new MySet<String>(); // set.add("abcd"); // set.add(8);//编译出错 // MySet<Integer> set = new MySet<Integer>(); // set.add("abcd");//编译出错 // set.add(8); } }
注意:当一个变量被声明为泛型时,只能被实例变量和方法调用,而不能被静态变量和方法调用。原因很简单,参数化的泛型是一些实例。静态成员是被类的实例和参数化的类所共享的,所以静态成员不应该有类型参数和他们关联。
三、Java泛型类
1、概念:当一个类要操作的引用数据类型不确定的时候,可以将该类型定义一个形参。用到的这类时,由使用者来通过传递类型参数的形式,来确定要操作的具体的对象类型。这意味着在定义这个类时,需要在类上定义形参,用于接收具体的类型实参。这就是将泛型定义在类上,即泛型类。
2、什么时候使用泛型类呢?
只要类中操作的引用数据类型不确定的时候,就可以定义泛型类。 有了泛型类,省去了曾经的强转和类型转换异常的麻烦。
代码示例:
/* * 泛型类的演示 */ public class GenericDemo2 { public static void main(String[] args) { MyVessel<Worker> u = new MyVessel<Worker>(); //u.setObject(new Student());//不行,u中存放的是Worker,实参只能是Worker类型 u.setObject(new Worker()); Worker w = u.getObject(); System.out.println(w); MyVessel<Student> v = new MyVessel<Student>(); //v.setObject(new Worker());//不行,实参只能是Student类型 } } class MyVessel<E>{ //从语法上讲把“E”取成别的名字如“QQ”也是可以的,但不规范。 private E obj; public void setObject(E obj){ this.obj = obj; } public E getObject(){ return obj; } } class Student{ String profession; } class Worker{ String company; }
四、Java泛型方法
1、泛型方法的定义(与类的泛型捆绑)
方法要操作的类型不确定的,但是和调用该方法的对象指定的类型是一致。
2、泛型方法的定义(独立于类的泛型)
方法要操作的类型不确定的,而且不一定和调用该方法的对象指定的类型是一致。
3、泛型方法的定义(静态方法的泛型)
静态方法不能访问类上定义的泛型,因为它没有对象。如果静态方法需要泛型,该泛型只能定义在方法上。
代码示例:
class Demo<W>{ //方法上的泛型和类的一致,或者说依赖于类的泛型 public void show(W w){ System.out.println("show:"+w.toString()); } //不带泛型,不安全。因为在调用方可以把该方法的返回值强转成其它类型,从而出现强转异常 public Object myprint0(Object a){ System.out.println("myprint:"+a); return a; } //方法带泛型,但要求和类的泛型相互独立。可以限定返回类型和方法的实参相同,更安全,而且不用强转。 public <A> A myprint(A a){ System.out.println("myprint:"+a); return a; } //静态方法带泛型,泛型一定要独立于类,因为它没有对象。 public static <A> A myprint2(A a){ System.out.println("myprint:"+a); return a; } }
五、Java泛型接口
这个不好解释直接代码示例了:
interface Inter<V>{ public abstract V show(V v); } //实现泛型接口的类的定义。 方法中的参数类型和类声明实现接口时指定的类型一致,且已经确定为String!-----本例假设我们写这个类的时候知道该类就是专门处理String型数据的 class InterImpl implements Inter<String>{ @Override public String show(String s) { System.out.println(s); return s; } } //实现泛型接口的类的定义。 方法中的参数类型和类声明实现接口时指定的类型一致,但不确定!-----本例假设我们写这个类的时候不知道该类是处理什么类型的数据的,但有一点确定:声明类对象时指定什么类型(泛型的实参),show方法就处理该类型 class InterImpl2<C> implements Inter<C>{ @Override public C show(C s) { System.out.println(s); return s; } }
六、Java泛型高级
(PS:关于Java泛型的高级应用,最低要求:虽然自己不一定能写出这样类似的定义,但是你必须会使用或调用(其他程序员或API中已经采用这种技术写的类或方法),所以这里只做最简单的介绍,有兴趣的博友可以去深入学习,博主能力有限,嘿嘿。。)
1、泛型的通配符:?
当操作的不同容器中的类型都不确定的时候,而且使用的都是元素从Object类中继承的方法, 这时泛型就用通配符?来表示即可。(助理解的比方: 泛型中的多态应用)
2、泛型的限定:
对操作的类型限制在一个范围之内。比如:定义一个功能,只操作Person类型或者Person的子类型。这时可以用:
? extends E:接收E类型或者E的子类型。这就是上限。
? super E: 接收E类型或者E的父类型。 这就是下限。
一般情况下:
只要是往容器中添加元素时,使用上限。 ? extends E
只要是从容器中取出元素时,是用下限。 ? super E
高级只有这么多,博友可以去API中看看这种情况,例如:Collection
版权声明:本文为博主原创文章,未经博主允许不得转载。