Java编程思想-Chapter15-泛型

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


概述

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

泛型的主要目的:数组

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

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

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

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

泛型接口

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

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

    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方法建立元组工具

      javapublic 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实用工具this

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

构建复杂模型

使用泛型能够简单而安全的建立复杂模型code

擦除

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

在java泛型代码内部,没法得到任何有关泛型参数类型的信息。所以,你没法知道用来建立某个特定实例的实际的类型参数。好比List和List在运行时是相同的类型。blog

擦除的正当理由是从非泛华代码到泛华代码的转变过程,以及不破坏现有类库的状况下,将泛型融入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);
}

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

相关文章
相关标签/搜索