Java的泛型机制是与C++不同的,它采用的是擦除机制,所以泛型在java中是一种折中的处理方案,而不是java本身的一种特性。

由于采用的擦除机制,所以在运行时,对使用泛型的对象进行类型判断是有很严重的问题的。例如:

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



list1和list2在运行时进行的类型判定,特别在反射操作时,这两个是相等的。


虽然有这样严重的问题,为什么还要采用擦除机制处理泛型?

主要是因为兼容性移植,泛型是在java的2版本出现的,属于后增加的内容,所以为了做到兼容移植和向前兼容,采用的擦除机制。


最简单的泛型使用方式大概就是直接指明泛型的具体类了,这样的操作会非常方便的在编译期可以检查容器类存储的数据是不是符合泛型的要求。还有另外的好处就是非常方便的组织数据的层级结构,就像XML的数据格式一样。例如:

List<Map<String,String>> list=new ArrayList<Map<String,String>>();

public List<String> getList(List<? extend Object> list){
  //add logic here
return list;

}



第一段是属于数据结构的组织和类型的检验,只要不符合这种组织结构的数据是不能存入到list当中的。

第二段则属于泛型构建的方法了,当然使用它也是有要求的,就是它可以被不同的类型共享。如果不存在类型共享的问题,不建议使用它。如果存在类型共享可以考虑使用它。在使用的时候,尽量使用泛型化的参数。如果仅仅只是泛型化的参数不能解决问题,再考虑泛型化的返回值。使用泛型化的返回值,可能还需要为不同的返回值类型提供不同的类类定义。这个时候泛型所构建的方法发生新的变化,如下所示:

List<T> getList(Map<String,T> map){
    List<T> result=new ArrayList<T>();
    //add logic here
    return result;
}

这个会让我们的代码看上去相当难以理解,特别是像我很早以前第一次接触到它的时候。在调用的时候,要符合方法的泛型定义。



泛型的一些高级的用法,如下所示:

第一种,直接指定泛型类型的:

List<T> getList(List<T> params){
    //add logic here
    return params;
}

当调用这个方法的时候,T必须显式的指明类型,Object类型是不可以省略掉的。这种属于直接指定泛型类型的。


第二种,继承类型的泛型:

List<? extend T> getList(List<? extend T> list){
//add logic here
return list;
}

当调用这个方法的时候,泛型的类型必须是T的子类,如果不是会出现编译错误。这种属于确定边界的泛型,确定的是上边界。


第三种,父类型的泛型

List<? super T> getList(List<? super T> list){
//add logic here
return list;
}

当调用这个方法的时候,泛型的类型必须是T的父类型。这种也是属于确定边界的泛型,确定的是下边界。


第四种,通配符类型的泛型

List<?> getList(List<?> list){
  //add logic here
   return list;
}

当调用这个方法的时候可以不用指定泛型的类型,也可以指定,这个是一种不设定任何边界的泛型。



原本泛型的主要思想是构建更加通用的代码,不过java对它的实现却让这种思想的实现更加的原理,而且想到泛型大概也就剩下在使用容器类的地方做类型约束。这个在我个人看来是理想被现实约束了!