Java Generic

什么是泛型:

       Generic programming means to write code that can be reused for objects of many different types. 摘自《 Core java  

 

为什么使用泛型

       主要是为了类型转换。如果不使用泛型,那么每次都得显式的就行类型转换。

如:

       不使用泛型:

       List list = …

    String str = (String)list.get(1);  

    使用泛型:

    List<String> list = …

    String str =  list.get(1);  

 

如何定义泛型:

       使用 <Type variable> 中带有类型,该类型可以是具体类型 . 比如 : String.

也可以不是具体类型,比如 <? Extends Number>. 具体类型 (reifiable type) 和非具体类型 (unreifiable type)

       (非)具体化类型后面再介绍

       < Type variable > 即表明声明了泛型,可以在类、方法、字段等处定义泛型。

 

什么是类型参数( type parameter   参数化类型( parameterized type

       比如,类 List<String> ,称 String 为类型参数。 List<String> 为参数化类型,因为 List String 参数化了。  

 

 

子类型( subtypes )及替换原则  

    Integer Number 的子类,能放置 Number 的地方都能放置 Integer ,这便是 Java 中的替换原则。

    Substitution Principle: a variable of a given type may be assigned a value of any subtype of that type, and a method with a parameter of a given type may be invoked with an argument of any subtype of that type -- 摘自 Java Generics & Collections  

    数组 Integer[] Number[] 的子类,那是不是 List<Integer> 也是 List<Number> 的子类?其实不是,他们其实是同一类型。可以通过以下代码判断。

List<Number> numList = …

List<Integer> intList = …

System.out.println(numList.getClass() == intList.getClass())  

       如果我们需要在泛型中拥有类似于数组的该特性( Integer[] Number[] 的子类),我们就得使用通配符 (wildcard).  
 

通配符 (wildcard) 访问原则

       上述说过,数组 Integer[] Number[] 的子类, List<Integer> 确不是 List<Number> 的子类。要实现类似于数组的该特性,就需要通配符来实现 : List< ? extends Number> .

       表示通配符,表示任意类型(继承 Object 的任意类型)。

       ? extends T 表示继承或实现 T 的类型。即 T 的任意子类型 , 比如 ? extends Number 表示继承自 Number 的任意类型,可以是 Integer, Double …

       ? super T 表示 T 的任意父类型( super type ),比如: ? Super ArrayList 表示 ArrayList 的任意父类型,可以是 List, AbstractList…

       表示 ? extends Object  
 

       通过通配符,我们可以实现类似于数组的功能:

       List<Integer>  intList = …;

       List<Number> numList=…;

       List<? Extends Number> list1 = intList;

       List<? Extends Number> list2 = numList;

       List<? Super Number> list3 = numList;  

       这样我们可以向数组一样进行类型转换。但却不能像数组一样进行以下方式的赋值:

       Number[] nums = …

       Nums[0] = Integer.valueOf(2)..  

       如果编写以下代码:

       List<? Extends Number> list =  … ;

       List.add(Integer.valueOf(2)) // 编译器报错。

       List.add(Double.valueOf(2)) // 编译器报错  

    为什么呢? <? Extends Number> 不是表示 Number 的任意子类型么? 使用替换原则不是应该正确的么?

       该问题的源头就是因为 <? Extends Number> 表示 Number 的子类型。但是我们却不知道到底是哪个具体的类型。因此 List< ? extends Number> 可以指向其任意的子类型。比如, List<Integer>, List<Number>,List<Double>. 如果是 list 是指向 ArrayList<Double>, list.add(Integer.valueOf(2)) 合法的话,那么 list 中就存在 Double Integer Number 子类型的对象, ArrayList<Double> 中的元素类型就不一样。那么 list.get(..) 就有可能返回 Integer 类,就可能出现问题,由于泛型有个基本原则:

    Cast-iron guarantee:

the implicit casts added by the compilation of generics never fail.

  •  
    •  
      •  
        •  

              -- 摘自 Java Generics & Collections  

所以编译器在编译级别就将可能导致类型转化的错误屏蔽掉。

    当然我们可以调用 list.add(null), 因为 null 是任意类型的子类型,可以安全的使用,但很多时候没有用处。 

       如果使用 list.get(..) 就不会有问题,因为 list 存的就是 Number 子类对象,任何子类都能转为其基类( Number )。  

       同样的道理,以下代码无法编译:

       List<? Super Integer> list = …

       Integer in =  list.get(0); // 编译过不去

       因为 List 存的是 Integer 的基类型,但是我们并不知道具体是哪个基类。所以就无法进行转化 , 当然我们可以调用 Object obj = list.get(0)

       如果使用 list.add(Integer.valueOf(2)) 却不会有问题,因为 list 存的是 Integer 的父类,那么必然可以存 Integer 类型。  

       因此利用 < ? extend …> <? Super …> 时,一般使用一个原则:

The Get and Put Principle: use an extends wildcard when you only get values out of a structure, use a super wildcard when you only put values into a structure, and don't use a wildcard when you both get and put.

  •  
    •  
      •  
        •  

              -- 摘自 Java Generics & Collections

类型参数 VS 通配符

       比如我们需要将一个list的数据拷贝到另一个list中。

假设方法在类Collections中定义:

public static <T> void copy(List<? super T> dst, List<? extends T> src)  

List<Object> objs = Arrays.<Object>asList(2, 3.14, "four");

List<Integer> ints = Arrays.asList(5, 6);  

List<Object> objs = Arrays.<Object>asList(2, 3.14, "four");

List<Integer> ints = Arrays.asList(5, 6);

Collections.copy(objs, ints);

assert objs.toString().equals("[5, 6, four]");  

由以下方法调用, <Object> 表示 T Object

Collections.copy(objs, ints);

Collections.<Object>copy(objs, ints);

Collections.<Number>copy(objs, ints);

Collections.<Integer>copy(objs, ints);  

比较以下几种方法签名:

1:public static <T> void copy(List<T> dst, List<T> src)

2:public static <T> void copy(List<T> dst, List<? extends T> src)

3:public static <T> void copy(List<? super T> dst, List<T> src)

4:public static <T> void copy(List<? super T> dst, List<? extends T> src)  

比较:

1 :限制最大,必须要求 dst src 是同一类型,比如同是 List<Integer>.

       该方法不能用于 上述的例子 collectioins.copy(objs,ints)  

2: 仅仅使用于T为Object时。即Collections.<Object>copy(objs, ints)方式  

3:仅仅使用于T为Integer时,Collections.<Integer>copy(objs, ints);  

4 :可应用于 T Object Number Integer 时。  

以上例子摘自 – Java Generic & Collections  

简单的总结一下:

       泛型方法签名尽可能的使用 wildcard ,其可用范围更广。如果需要严格限制为同一类型时,就可以使用 type parameter  

 

 

Wildcard capture

       译为 捕捉通配符??? 何为 wildcard capture. 顾名思义 : 就是捕捉通配符的类型。 看以下例子:

       Pair<T>有first 和second两个变量。分别有对应的set, get 方法。  

       public static void swap(Pair<?> p){

             ? t = p.getFirst(); // ERROR

             p.setFirst(p.getSecond());

             p.setSecond(t);  

       }

       但是用T确实没有问题:

public static <T> void swapHelper(Pair<T> p)

{

   T t = p.getFirst();

   p.setFirst(p.getSecond());

   p.setSecond(t);

}  

因此我们经常使用swapHelper(Pair<T> p)来实现swapHelper(Pair<?> p):

public static void swap(Pair<?> p) { swapHelper(p); }  

这种情况,我们叫T 捕捉了 通配符(T captures the wildcard).我们并不知道wildcard表示什么类型,但是我们知道其必然是一个明确的类型。因此可以用T去捕捉它。(这里有点绕,知道T可以捕捉?即可)。  

       注意的是:要用T捕捉?,那么要保证wildcard必须是表示同一、明确的类型(尽管我们不知道这是什么类型)。

       比如 ArrayList<List<T>>就不能用来捕捉ArrayList<List<?>>,因为ArrayList可能包含两个或以上List<?>,每个List<?>拥有不同的类型。  

以上例子摘自—Core Java  
 

擦除( erasure  

泛型定义:  

泛型方法:  

Bridge Method  

具体化类型 (reifiable type)  

泛型对象创建  

消除 Unchecked warning  

反射与泛型  

Effective generic

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值