泛型

 

1、Java的泛型是类型擦除的 
  Java中的泛型是在编译期间有效的,在运行期间将会被删除,也就是所有泛型参数类型在编译后都会被清除掉.请看以下例子 

  public static void test(List<Integer> testParameter) {          } 

  public static void test(List<String> testParameter) {            } 

  以上是一个最最简单的重载例子,在编译后泛型类型是会被擦除的,所以无论是List<Integer>List<String>都会变成List<E>,所以参数相同,重载不成立,无法编译通过,而且读者可以尝试将不同泛型的类getClass()看看,他们的Class是一样的 

2、不能初始化泛型参数和数组 
      请观察以下代码:

  class Test<T> {      

    private T t = new T();            //编译不通过       

    private T[] tArray = new T[5];        //编译不通过     

    private List<T> list = new ArrayList<T>();  //编译通过  

  }  

  Java语言是一门强类型,编译型的安全语言,它需要确保运行期的稳定性和安全性,所以在编译时编译器需要严格的检查我们所声明的类型,在编译期间编译器需要获取T类型,但泛型在编译期间已经被擦除了,所以new T()new T[5]都会出现编译错误,而为什么ArrayList却能成功初始化?这是因为ArrayList表面是泛型,但在编译期间已经由ArrayList内部转型成为了Object,有兴趣的读者可以看一下ArrayList的源码,源码中容纳元素的是private transient Object[] elementData,Object类型的数组 

3、强制声明泛型的实际类型 
  在使用泛型时,在大多数情况下应该声明泛型的类型,例如该集合是存放User对象的,就应该使用List<User>来声明,这样可以尽量减少类型的转换,若只使用List而不声明泛型,则该集合等同于List<Object> 

4、注意泛型没有继承说 

  public static void main(String[] args) {       

    List<Object> list = new ArrayList<Integer>();     // 编译不通过      

    List<Object> list = new ArrayList<Double>();      //编译不通过 

  }  

  上面代码的业务逻辑为有一组元素,但我不确定元素时整形还是浮点型,当我在确定后创建对应的泛型实现类,刚接触泛型的读者有可能会犯以上错误,理解为泛型之间是可以继承的,例如声明Object的泛型实际上创建Integer泛型,错误认为泛型之间也存在继承关系,但这是不正确的,泛型是帮助开发者编译期间类型检查安全.我们可以换种方式实现以上业务逻辑 

  public static void main(String[] args) {        

    List<Number> numberList = new ArrayList<Number>();              //Number 为Integer 和 Double 的公共父类    

    List<? extends Number> numberList2 = new ArrayList<Integer>();     //使用通配符,代表实际类型是Number类型的子类      

    List<? extends Number> numberList3 = new ArrayList<Double>();     //or  

  }  


5.不同场景使用不同的通配符 
     泛型当中支持通配符,可以单独使用'?' 来表示任意类型,也可以使用刚才上面例子中的extends关键字表示传入参数是某一个类或接口的子类,也可以使用super关键字表示接受某一个类型的父类型,extendssuper的写法一样,extends是限定上界,super为限定下界 

6.建议采用顺序为List<T> List<?> List<Object> 
      以上三者都可以容纳所有的对象,但使用的顺序应该是首选List<T>,然后List<?>,最后才使用List<Object>,原因是List<T>是确定为某一类型的,安全的,也是Java所推荐的,List<?>代表为任意类型,List<T>类似,List<Object>中全部为Object类型,这与不使用泛型无异,List<T>是可读可写的,因为已经明确了T类型,而其他两者在读取时需要进行向下转型,造成了不必要的转型 

7.使用多重边界限定 
      现在有一个业务需求,收钱时需要是我们本公司的员工(继承User),同时亦需要具备收银员的功能(实现Cashier),此时我们当然可以想到接受1User然后判断User是否实现了Cashier接口,但在泛型的世界中,我们可以怎么做?请看以下例子  

  public static  <T extends User & Cashier> void CollectMoney(T t){    }  


      以上例子就表明,限定了上界,只要是UserCashier的子类才可以作为参数传递到方法当中 

 

posted on 2016-02-01 15:32  快跑的小鸡  阅读(308)  评论(0编辑  收藏  举报

导航