1

虽是读书笔记,但是如转载请注明出处http://segmentfault.com/blog/exploring/
..拒绝伸手复制党


概述

Java泛型核心概念: 告诉编译器想使用什么类型,然后编译器帮你处理一切细节。

泛型的主要目的:

  1. 制定容器要持有什么类型对象,而且由编译器来保证类型的正确性。
  2. 简单而安全的创建复杂模型

泛型不能做什么:不能显示地引用运行时类型操作,例如转型,instanceof and new表达式。(因为擦除)

简单泛型(三个使用场景)

  • 一个元组类库:创建元组,返回一组任意类型的对象。(它将一组对象直接打包存储于其中一个单一对象)
  • 一个堆栈类:
  • RandomList :构建一个可以应用于各种类型的对象的工具。

泛型接口

泛型方法的几个应用场景:

  1. 杠杆利用参数推断
  2. 可变参数(T ... args)与泛型方法(makelist实现util.Arrays.asList方法相同的功能)
    用于Generator的泛型方法: 使用泛型方法创建Generator对象,大大减少了我们要编写的代码

    javapublic BasicGenerator(Class<T> type){
           this.type = type;
          }
     public T next(){
           try{
               return type .newInstance();
               } catch(Exception e){
               throw new RuntimeException(e);
                 }
            }
    }
    
    1. 简化元组的使用 : 通过使用泛型方法整个各个Tuple类,重载static方法创建元组

      java public class Tuple {
            public static <A,B> TwoTuple<A,B> tuple(A a, B b) {
                return new TwoTuple<A,B>(a, b); }
            public static <A,B,C> ThreeTuple <A,B,C> tuple(A a, B b, C c) {
                return new ThreeTuple<A,B,C>(a, b, c);}
            public static <A,B,C,D> FourTuple<A,B,C,D> tuple(A a, B b, C c, D d) {
                return new FourTuple<A,B,C,D>(a, b, c, d); }
           }
      
    2. 一个Set实用工具

泛型用于内部类和匿名内部类

构建复杂模型

使用泛型可以简单而安全的创建复杂模型

擦除

Java泛型是通过擦除来实现的,namley 在使用泛型的时候,任何具体的类型信息都被擦除了,你唯一知道的就是你在使用一个对象。

在java泛型代码内部,无法获得任何有关泛型参数类型的信息。因此,你无法知道用来创建某个特定实例的实际的类型参数。比如List和List在运行时是相同的类型。

擦除的正当理由是从非泛华代码到泛华代码的转变过程,以及不破坏现有类库的情况下,将泛型融入Java语言。擦除使得现有的非泛型客户端代码能够在不改变的情况下继续使用,直至客户端准备好用泛型重写这些代码。

边界处的动作: 非泛型和泛型版本的相似的两个类通过javap -c 命令反编译可以发现字节码是相同的,就是说在运行时使用泛型的代码和普通代码没有什么区别。泛型中的所有动作都发生在边界处—对传递进来的值进行额外的编译期检查,并插入对传递出去的值的转型。这有助于澄清对擦除的混淆,所谓边界,就是发生动作的地方。

擦除的补偿

泛型不能做:

  1. instanceof
    解决方法:使用类型标签,利用动态的isInstance判断

    javapublic class ClassTypeCapture<T> {
          Class<T> kind; //类型标签
           public ClassTypeCapture(Class<T> kind){
                 this.kind = kind;
          }
    }
    
  2. 泛型不能做:new表达式
    解决方法:传递一个显示的工厂对象,限制其类型,使得只能接受实现这个工厂的类。

    javapublic interface FactoryI<T> {
          T create();
    }
    public class Foo2<T> {
           private T x ;
           //工厂
           public <F extends FactoryI<T>> Foo2(F factory){
                 x = factory.create();
          }
           public static void main(){
                 new Foo2<Integer>(new IntegerFactory());
                 new Foo2<Widget>(new Widget.Factory());
          }
    }
    
  3. 泛型不能做:创建数组 T[] array
    解决方法: 想要创建泛型数组的时候都使用ArrayList. orz...或者使用类型标记

如果实在想创建泛型数组,那么唯一方式就是创建一个被擦除类型的新数组(对象数组),然后对其转型。

javapublic class GenericArray<T> {
       private T[] array ;
       public GenericArray(int sz){
             //创建一个对象数组,然后对它转型
             array = (T[])new Object[sz] ;
      }
}

但是这样并不是很好,因为有了擦除,数组运行时候类型就只能是Object[],如果在创建时候对其转型为T[],那么编译器该数组的实际类型就会丢失,而编译器可能会错过潜在的错误检查。所以最为可靠的方式:在集合内部使用Object[],然后当你使用数组元素时,添加一个对T的转型。

javaprivate Object[] array;

public T get(int index) { 
     return (T) array[index];//Object转型为T
 } 

public T[] rep() {
     return (T[])array; //Object转型为T
}

所以最终解决办法是使用类型标记:

javaT[] array ;
public Constructor(Class<T> type, int sz) {
array = (T[]) Array.newInstance(type, sz);
}

边界
通配符
问题
自限定的类型
动态类型安全
异常 ....


SecondLife
1.9k 声望252 粉丝

引用和评论

0 条评论