Java泛型

一、泛型概念

      泛型是JavaSE1.5的新特效,泛型的本职是参数化类型,就是说所操作的数据类型被指定为一个参数,这种参数可以用在类、接口和方法中创建,分别称为泛型类、泛型接口、泛型方法。引用泛型的好处是安全简单。

     泛型机制将类型转换时的类型检查从运行时提前到了编译时,使用泛型编写的代码比使用object时强制类型转换的机制具有更好的可读性和安全性。

二、为何引入泛型

     JDK5以前,对象保存到集合中,取出需进行类型的强制转换,这样不可避免就会引发程序的一些安全性问题,例如:

     

     出现上面错误是因为list默认的类型为Object类型,在之后的循环中,由于之前在list中也加入了Integer类型的值或其他编码原因。编译阶段正常,而运行时会出现“java.lang.ClassCastException”异常。因此,导致此类错误编码过程中不易发现。

     使用泛型

      

  编译器会在编译时报错,集合内只能存储java.lang.String类型的实例

三、泛型作用

      泛型是提供给javac编译器使用的,它用于限定集合的输入类型,让编译器在源代码级别上,即挡住向集合中插入非法数据。但编译器编译完带有泛形的java程序后,生成的class文件中将不再带有泛形信息,以此使程序运行效率不受到影响,这个过程称之为“擦除”。例如:

     

使用泛型时需要注意的问题:

   1.参数化类型不考虑类型参数继承关系:

       List<String> list = new ArrayList<Object>();   //error

       List<Object> list = new ArrayList<String>();  //error

  2.使用泛形时,泛形类型须为引用类型,不能是基本数据类型

  3.在创建数组实例时,数组元素不能是参数化类型

 四、泛型的通配符扩展应用

  4.1 通配符

   问题:定义一个方法,接收一个任意集合,并打印出集合中的所有元素,如下所示:

   

l.由于print方法c参数的类型为Collection<?>是一种不确定的类型,因此在方法体内不能调用与类型相关的方法,例如add()方法。
2.总结:使用?通配符主要用于引用对象,使用了?通配符,就只能调对象与类型无关的方法,不能调用对象与类型有关的方法

 4.2 限制的通配符

a. 限定通配符的上边界:(?必须是Number的子类)
b.限定通配符的下边界 : (?必须是Integer的父类)

 4.3 自定义泛型方法

    泛型方法定义规则:

    Java程序中的普通方法、构造方法和静态方法中都可以使用泛型。方法使用泛形前,必须对泛形进行声明,语法:<T>,T可以是任意字母,但通常必须要大写。<T>通常需放在方法的返回值声明之前。

    泛型方法定义:

public static <T> T marshalle(T arg){}

  泛型方法定义注意问题:

    1.只有对象类型才能作为泛型方法的实际参数

    2.在泛型中可以同时有多个类型

五.自定义泛型类

如果一个类多处都要用到同一个泛型,这时可以把泛形定义在类上(即类级别的泛型)
语法格式如下:

加上限定符,就可以访问限定类型的方法,类型更明确

注:我们知道final类不可继承,在继承机制上class SomeString extends String是错误的,但泛型限定符使用时是可以的:<T extends String>,只是会给一个警告。

后面的通配符限定有一个super关键字,这里没有。

六、泛型擦除

泛型只在编译阶段有效,编译后类型被擦除了,也就是说jvm中没有泛型对象,只有普通对象。所以完全可以把代码编译为jdk1.0可以运行的字节码。

擦除的方式:

  1.定义部分,即尖括号中间的部分直接擦除

public class GenericClass<T extends Comparable>{}
   擦除后:
public class GenericClass{}

2.引用部分如:
 public T field1;
   其中的T被替换成对应的限定类型,擦除后:
 public Comparable field1;

3.如果没有限定类型:
 public class GenericClass<T>{ 
   public T field1;
 }
那么的替换为object,即:
public class GenericClass{ 
   public Object field1; 
}

4.有多个限定符的,替换为第一个限定类型名。如果引用了第二个限定符的类对象,编译器会在必要的时候进行强制类型转换
public class GenericClass<T extends Comparable & Serializable>{
   public T field1;
}
   类擦除后变为:
public class GenericClass{ 
  public Comparable field1; 
}

而表达式返回值返回时,泛型的编译器自动插入强制类型转换。

七、泛型的约束和限制

  不能使用8个基本类型实例化类型参数

  原因在于类型擦除,Object不能存储基本类型:byte,char,short,int,long,float,double,boolean

  从包装类角度来看三个: Number(byte,short,int,long,float,double),char,boolean

类型检查不可使用泛型:
if(aaa instanceof Pair<String>){}//error
Pair<String> p = (Pair<String>) a;//warn
Pair<String> p; 
Pair<Integer> i; 
i.getClass()==p.getClass();//true
不能创建泛型对象数组
GenericMethod<User>[] o=null;//ok 
o=new GenericMethod<User>[10];//error
可以定义泛型类对象的数组变量,不能创建及初始化。
注,可以创建通配类型数组,然后进行强制类型转换。不过这是类型不安全的。
o=(GenericMethod<User>[]) new GenericMethod<?>[10];
不可以创建的原因是:因为类型擦除的原因无法在为元素赋值时类型检查,因此jdk强制不允许。
有一个特例是方法的可变参数,虽然本质上是数组,却可以使用泛型。
安全的方法是使用List。

注:

1. 泛型类中,<T>称为类型变量,实际上就相当于在类中隐形的定义了一个不可见的成员变量:`private T t;`,这是对象级别的,对于泛型类型变量来说是在对象初始化时才知道其具体类型的。而在静态域中,不需要对象初始化就可以调用,这是矛盾的。

2. 静态的泛型方法,是在方法层面定义的,就是说在调用方法时,T所指的具体类型已经明确了。

posted @ 2017-04-22 13:44  华子AI  阅读(274)  评论(0编辑  收藏  举报