Java新特性2 -- 泛型

来源:互联网 发布:姿态变换矩阵 编辑:程序博客网 时间:2024/05/20 21:49

在我们开发中,经常会用到集合,我们也知道集合是用来存储元素的,但它却没有规范存储元素的具体类型,为了是我们的程序更规范化,就应当使用泛型了。

泛型是提供给javac编辑器使用的,可以限定集合中的输入类型,让编译器挡住源程序的非法输入,编辑器编译带类型说明的集合时会去除掉“类型”信息,是程序运行效率不受影响,对于参数化的泛型类型,getClass()方法的返回值和原始类型完全一样,由于编译生成的字节码会去掉泛型的类型信息,只要能跳过编译器,就可以往某个泛型集合中加入其他类型的数据,例如:用反射得到集合,在调用其add方法即可。

       ArrayList<String> list1 = new ArrayList<String>();

       ArrayList<Integer> list2= new ArrayList<Integer>();

       System.out.println(list1.getClass() == list1.getClass());

打印true,就表示泛型只提供给编译器使用,运行期是不受影响,那我们想在list2中添加String类型的对象,如果我们直接add方法,编译器会报错。那我们怎么办?我们可以使用反射得到集合,然后调用其add方法就可以添加String类型的对象

       list2.getClass().getMethod("add",

Object.class).invoke(list2, "abc");

       System.out.println(collection3.get(0));

这样我们就在list2中添加了一个String类型的对象。

下面我们来简单了解一下泛型

l         术语,例如ArrayList<E>类定义和ArrayList<Integer>类定义

n         整个称为ArrayList<E>泛型类型

n         ArrayList<E>中的E称为类型变量或类型参数

n         整个ArrayList<Integer>称为参数化的类型

n         ArrayList<Ingerger>中的Integer称为类型参数的实例或实际类型参数

n         ArrayList<Integer><>念着typeof

n         ArrayList称为原始类型

l         参数化类型与原始类型的兼容性

n         参数化类型可以引用一个原始类型的对象,编译报告警告,但没有错,例如:Collection<String> c = new Vector();

n         原始类型可以引用一个参数化类型的对象,编译报告警告,但没有错,例如:Collection c = new Vector<String> ();

l         参数化类型不考虑类型参数的继承关系:

n         Vector<String> v = new Vector<Object>(); 错误

n         Vector< Object > v = new Vector<String>(); 错误

l         在创建数组实例时,数组的原始不能使用参数化的类型,例如:

Vector<Integer> v[] = new Vector<Integer>[10];错误

练习:定义一个方法,该方法用于打印出任意参数化类型的集合中的所有数据,该方法如何定义?

错误方式:

    public void printCollection(Collection<Object> collection){

       for(Object obj : collection){

           System.out.println(obj);

       }

    }

为什么错呢?

由于泛型不考虑类型参数的继承关系,如果我们调用这个方式传递的是

Collection<Integer> list2= new ArrayList<Integer>();

那肯定会报错,这个方法是不能打印任意参数化类型的集合

正确方式:

    public void printCollection(Collection<?> collection){

       for(Object obj : collection){

           System.out.println(obj);

       }

    }

这个方法能打印出任意参数化类型的集合,但是collection不能调用add方法,为什么?

因为使用?通配符可以引用其他各种参数化的类型,?通配符定义的变量主要用作引用,可以调用与参数化无关的方法,不能调用与参数化有关的方法,例如add方法就是与参数化有关的方法。

使用?通配符表示可以使用任意参数化类型,如果我们需要一个范围,那该怎么办呢?在?通配符中还有两个上边界与下边界,分别为:限定通配符上边界和限定通配符下边界。

l         限定通配符上边界

Vector<? extends Number> x = new Vector<Integer>(); 正确

Vector<? extends Number> x = new Vector<String>();  错误

l         限定通配符下边界

Vector<? super Integer> x = new Vector<Number>(); 正确

Vector<? super Integer> x = new Vector<Byte>();   错误

我们了解泛型的一些知识后,现在我们来自己定义泛型,泛型方法与泛型类

l         自定义泛型方法

    //将任意类型的数组中的所有元素填充为相应类型的某个对象

private <T> void fillArray(T[] a,T obj){

       for(int i=0;i<a.length;i++){

           a[i] = obj;

       }

    }

    //Object对象转化成任意对象

    private <T> T autoConvert(Object obj){

       return (T)obj;

    }

    //将任意类型的数组调换位置

    private <T> void swap(T[] a,int i,int j){

       T tmp = a[i];

       a[i] = a[j];

       a[j] = tmp;

    }

   

    private <T> T add(T x,T y){

       return null;

    }

调用代码:

add(3,5);

       Number x1 = add(3.5,3);

       Object x2 = add(3,"abc");

返回类型必须为我们传递参数的最小公共类型

       String[] strArray=new String[]{"a","b","c"};

       swap(strArray,1,2); //这样就可以实现数组交换

但是下面这种情况是不行

       int[] intArray=new int[]{1,2,3};

       swap(intArray,1,2);

因为只有引用类型才能作为泛型方法的实际参数,那为什么上面add怎么就可以呢?因为add方法中传递int值会自动装箱和拆箱,而我们这里是一个int[]类型是不会自动装箱和拆箱,因此编译器会报错

我们在定义泛型是可以使用extends限定符,我们可以参考java.lang.Class类的一个方法:

public <A extends Annotation> A getAnnotation(Class<A> annotationClass)

并且可以用&来指定多个边界,如:<V extends Serializable&Thread>

普通方法、构造方法和静态方法中都可以使用泛型;也可以抛出异常泛型,例如:

public <T extentds Exception> exceptionTest throws T{

try{

 

}catch(Exception e){

    throw (T)e;

}

 

}

在泛型中可以同时有多个类型参数,在定义他们的尖括号中用逗号分,例如:

public <K,V>  V getValue(K key){}

 

 

l         自定义泛型类型

如果类的实例对象的多处都要用到同一个泛型参数,即这些地方引用的泛型类型要保持同一个实际类型时,这时候就要采用泛型类型的方式进行定义,也就是类级别的泛型。例如:

public class GenericDao<E>  {

    public void add(E x){      

    }  

    public E findById(int id){

       return null;

    }  

    public void delete(E obj){     

    }  

    public void delete(int id){    

    }     

    public void update(E obj){     

    }  

    public static <E> void update2(E obj){   

    }  

    public E findByUserName(String name){

       return null;

    }

    public Set<E> findByConditions(String where){

       return null;

    }

}

注意:

n         在对泛型类型进行参数化时,类型参数的实例必须是引用类型,不能是基本类型

n         当一个变量被声明为泛型时,只能被实例变量和方法调用(还有内嵌类型),而不能被静态变量和静态方法调用。因为静态成员是被所有参数化的类所共享,所以静态成员不应该有类级别的类型参数。

原创粉丝点击