介绍 泛型是Java SE 1.5的新特性,泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。这种参数类型可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口、泛型方法。 Java语言引入泛型的好处是安全简单。 在Java SE 1.5之前,没有泛型的情况的下,通过对类型Object的引用来实现参数的“任意化”,“任意化”带来的缺点是要做显式的强制类型转换,而这种转换是要求开发者对实际参数类型可以预知的情况下进行的。对于强制类型转换错误的情况,编译器可能不提示错误,在运行的时候才出现异常,这是一个安全隐患。 泛型的好处是在编译的时候检查类型安全,并且所有的强制转换都是自动和隐式的,提高代码的重用率。 规则和限制 1、泛型的类型参数只能是类类型(包括自定义类),不能是简单类型。 2、同一种泛型可以对应多个版本(因为参数类型是不确定的),不同版本的泛型类实例是不兼容的。 3、泛型的类型参数可以有多个。 4、泛型的参数类型可以使用extends语句,例如<T extends superclass>。习惯上称为“有界类型”。 5、泛型的参数类型还可以是通配符类型。例如Class<?> classType = Class.forName("java.lang.String"); 代码实例 1、类型擦除 正确理解泛型概念的首要前提是理解类型擦除(type erasure)。 Java中的泛型基本上都是在编译器这个层次来实现的。在生成的Java字节代码中是不包含泛型中的类型信息的。使用泛型的时候加上的类型参数,会被编译器在编译的时候去掉。这个过程就称为类型擦除。 Collection<Integer> con1=new ArrayList<Integer>(); con1.add(1); con1.add(2); Collection<String> con2=new ArrayList<String>(); con2.add("abc"); con2.add("def"); //打印结果为true,泛型只是存在于编译期,运行期就不存在了 ,即con1和con2在内存中的字节码是同一个 System.out.println(con1.getClass()==con2.getClass()); con1.getClass().getDeclaredMethod("add", Object.class).invoke(con1, "test"); //有打印的结果可以看出来泛型只是存在于编译期 System.out.println(con1);//打印结果为[1, 2, test]有int和string 2、参数化类型不考虑类型参数的继承关系 //Vector<Object> v=new Vector<String>();//错误(编译器不通过) Vector v1=new Vector<String>();//体现参数化类型和原始类型的兼容性 Vector<Object> v2=v1; 3、泛型中的?通配符 通配符?所代表的其实是一组类型,但具体的类型是未知的。List<?>所声明的就是所有类型都是可以的。但是List<?>并不等同于List<Object>。List<Object>实际上确定了List中包含的是Object及其子类,在使用的时候都可以通过Object来进行引用。而List<?>则其中所包含的元素类型是不确定。其中可能包含的是String,也可能是 Integer。如果它包含了String的话,往里面添加Integer类型的元素就是错误的。正因为类型未知,就不能通过new ArrayList<?>()的方法来创建一个新的ArrayList对象。因为编译器无法知道具体的类型是什么。但是对于 List<?>中的元素确总是可以用Object来引用的,因为虽然类型未知,但肯定是Object及其子类。 //定义一个方法,用于打印出任意参数化类型的集合中的所有数据 private static void printValue(Collection<?> collection){ System.out.println(collection.size()); for (Object object : collection) { System.out.print(object); } } Collection<Integer> con1=new ArrayList<Integer>(); con1.add(1); con1.add(2); printValue(con1);//打印结果为1 2 Collection<String> con2=new ArrayList<String>(); con2.add("abc"); con2.add("def"); printValue(con2);//打印结果为abcdef 4、泛型中的通配符的扩展( 提示:限定通配符总是包括自己) //限定通配符的上边界 (限定了number的子类) Vector<? extends Number> v3=new Vector<Integer>(); //Vector< ? extends Number> v9=new Vector<String>();//错误 //限定通配符的下边界 Vector<? super Integer> v4=new Vector<Number>(); //Vector< ? super Integer> v5=new Vector<Double>();//错误 5、类似C++的泛型 使用<T>来声明一个类型持有者名称,然后就可以把T当作一个类型代表来声明成员、参数和返回值类型。 当然T仅仅是个名字,这个名字可以自行定义。 //1、 打印出输入的参数的类型 public static <T> void printType(T obj){ System.out.println(obj.getClass().getName()); } printType(new Integer(3));//打印出java.lang.Integer //2、定义一个方法可以将任意类型的数组中的所有元素填充为相应类型的某个对象 public static <T> void fillArray(T[]a,T obj){ for (int i = 0; i < a.length; i++) { a[i]=obj; System.out.println(a[i]); } } String[]str=new String[5]; //因为不支持基本数据类型这里用string演示 fillArray(str,"as"); 6、通过反射得到泛型的实际类型(很多框架底层的机制) public class GenericDemo { public static void main(String[] args) throws Exception { Method ms=GenericDemo.class.getMethod("apply", Vector.class);//传入方法名和参数 Type[] types= ms.getGenericParameterTypes();//获得泛型的类型 ParameterizedType ty=(ParameterizedType) types[0];//获得第一个 System.out.println(ty.getRawType());//获得原始类型 System.out.println(ty.getActualTypeArguments()[0]);//获得泛型类型 } //模拟的一个类的方法 public static void apply(Vector<Date> v){ } }