参考文章:http://www.infoq.com/cn/articles/cf-java-generics
1、Java泛型是Java5以后才出现的特性,主要是为了代码重用的同时在编译器来避免类型转换问题。泛型的存在的意义在于类型替换,和禁止一些可能出现类型转换问题的编码,在编译期就报错
2、Java源代码首先通过编译得到class字节码文件,然后在jvm中运行。java class文件中是不存在类型信息的,也就是说开始定义的泛型都会在编译后消失。这就是类型檫除,Java中的泛型基本上都是在编译器这个层面上实现的。例如
List<String>和List<Integer>等类型在编译之后都变成List。因此有以下几种特性:
(1)只存在List.class,而不存在List<String>.class,List<Integer>.class
(2)静态变量是由类的所有实例共享的,因此不能定义泛型的静态变量
- public class MyClass<T>{
- private static T s;//编译错误
- }
(3)异常类是在运行时抛出,通过catch捕获,但是运行时类型信息被抹去,所以对于MyException<String>和MyException<Integer>对于catch来说无法分辨
3、编译期间,对于泛型T来说,先用object进行替换,如果利用extends定义了上界,那么就利用上界类进行替换。
4、通配符?和上下界extends、super
(1)通配符?表示类型未知,因此可以传入任意类型。从语法层面上规定了通配符指定的对象不能实例化,因为类型未知。例如new ArrayList<?>()是错误的。下面例子中的alist.add(1)也是禁止的,因为List类型未知。试图对一个带通配符的泛型类进行操作都会出现编译错误。它和List<Object>的区别在于,即使是Object也是类型已知可以进行操作和实例化,所以下面代码中blist.add(1)是不会报错的。如果参数是List<Object>,传入List<String>,虽然理解上是可以的,但是编译器为了避免出现blist.add(1)这种在String列表中加入Integer的行为导致的ClassCastException,所以禁止这样做。
(2)定义泛型后,就给原来的继承体系增加了一个维度。例如Collection<? extends Number> ,List<Integer>,Set<Integer>和List<Double>,Set<Double>都是其子类,都可以作为匹配参数传入
下面的代码
- public class GenericTest<T extends Number> {
- public void get(List<?> alist) {
- alist.get(0);
- alist.add(1);//?通配符,表示类型未定,编译器禁止add
- }
- public void getO(List<Object> blist) {
- blist.add(1);//在该函数内部,list加入一个整型是没错的
- }
- public void test() {
- get(new ArrayList<String>());//正确
- get(new ArrayList<Integer>());//正确
- getO(new ArrayList<String>());//编译错误,因为可能会出现潜在的类型转换问题
- }
- }
(3)?extends XX限定了?匹配的上界,只有XX及其子类可以通过匹配。其主要应用场景是可以get得到XX的引用,来调用XX指定的方法,但是不能直接add,因为传入的参数确定的类型是其子类,add父类是不行的
(4)?super XX限定了?匹配的下界,只有XX及其父类可以通过匹配。其主要应用可以add加入XX的实例,因为XX是下界,传入的只能是XX及其父类,向上转型是可以的。但是不能get到XX的引用
- public class GenericTest<T extends Number> {
- public void get1(List<? extends B> alist) {
- }
- public void get2(List<? super B> alist) {
- }
- public void test() {
- get1(new ArrayList<A>());//编译错误,B或者其子类
- get1(new ArrayList<B>());
- get1(new ArrayList<C>());
- get2(new ArrayList<A>());
- get2(new ArrayList<B>());
- get2(new ArrayList<C>());//编译错误,B或者其父类
- }
- public void upperBound(List<? extends Date> list, Date date)
- {
- Date now = list.get(0);
- System.out.println("now==>" + now);
- //list.add(date); //这句话无法编译
- list.add(null);//这句可以编译,因为null没有类型信息
- }
- //可能的调用导致上界约束的add方法错误
- public void testUpperBound()
- {
- List<Timestamp> list = new ArrayList<Timestamp>();
- Date date = new Date();
- upperBound(list,date);
- }
- public void lowerBound(List<? super Timestamp> list)
- {
- Timestamp now = new Timestamp(System.currentTimeMillis());
- list.add(now);
- //Timestamp time = list.get(0); //不能编译
- }
- //可能的调用导致下界约束get方法错误
- public void testLowerBound()
- {
- List<Date> list = new ArrayList<Date>();
- list.add(new Date());
- lowerBound(list);
- }
- }
- class A {
- }
- class B extends A {
- }
- class C extends B {
- }
5、泛型方法:是否拥有泛型方法与是否在泛型类中无关,只需在返回值前加上<T>就行
- public class GenericTest {
- public <T> List<T> get(T t) {
- return new ArrayList<T>();
- }
- public void test() {
- String s = "";
- List<String> list = get(s);
- list.add("tt");
- list.add("ee");
- for(String a:list){
- System.out.println(a);
- }
- }
- public static void main(String[] args) {
- GenericTest test=new GenericTest();
- test.test();
- }
- }
第23条:请不要在新代码中使用原生态类型
如果使用原生态类型,就失掉了泛型在安全性和表述性方面的所有优势