【黑马程序员】java-泛型篇

来源:互联网 发布:开票软件 数据库文件 编辑:程序博客网 时间:2024/05/12 12:44
------- android培训、java培训、期待与您交流! ----------


java泛型介绍:

       Java泛型(generics)是JDK 5中引入的一个新特性,允许在定义类和接口的时候使用类型参数(type parameter)。

       声明的类型参数在使用时用具体的类型来替换。泛型最主要的应用是在JDK 5中的新集合类框架中。对于泛型概念的引入,开发社区的观点是褒贬不一。从好的方面来说,泛型的引入可以解决之前的集合类框架在使用过程中通常会出现的运行时刻类型错误,因为编译器可以在编译时刻就发现很多明显的错误。而从不好的地方来说,为了保证与旧有版本的兼容性,Java泛型的实现上存在着一些不够优雅的地方。当然这也是任何有历史的编程语言所需要承担的历史包袱。后续的版本更新会为早期的设计缺陷所累。  


类型擦除:

       正确理解泛型概念的首要前提是理解类型擦除(type erasure)。 Java中的泛型基本上都是在编译器这个层次来实现的。在生成的Java字节代码中是不包含泛型中的类型信息的。使用泛型的时候加上的类型参数,会被编译器在编译的时候去掉。这个过程就称为类型擦除。

       如在代码中定义的List<Object>和List<String>等类型,在编译之后都会变成List。JVM看到的只是List,而由泛型附加的类型信息对JVM来说是不可见的。Java编译器会在编译时尽可能的发现可能出错的地方,但是仍然无法避免在运行时刻出现类型转换异常的情况。类型擦除也是Java的泛型实现方式与C++模板机制实现方式之间的重要区别。

下面代码在运行期添加元素,避过泛型的类型检查:

      ArrayList<Integer> collection3 = new ArrayList<Integer>();
      System.out.println(collection3.getClass() == collection2.getClass());
     //collection3.add("abc");
      collection3.getClass().getMethod("add", Object.class).invoke(collection3, "abc");

     System.out.println(collection3.get(0));


泛型在使用中还有一些规则和限制
      1、泛型的类型参数只能是类类型(包括自定义类),不能是简单类型。
      2、同一种泛型可以对应多个版本(因为参数类型是不确定的),不同版本的泛型类实例是不兼容的。
      3、泛型的类型参数可以有多个。
      4、泛型的参数类型可以使用extends语句,例如<T extends superclass>。习惯上成为“有界类型”。
      5、泛型的参数类型还可以是通配符类型。例如Class<?> classType = Class.forName(java.lang.String);

通配符与上下界

       在使用泛型类的时候,既可以指定一个具体的类型,如List<String>就声明了具体的类型是String;

也可以用通配符 ? 来表示未知类型,如List<?>就声明了List中包含的元素类型是未知的。 

       通配符所代表的其实是一组类型,但具体的类型是未知的。List<?>所声明的就是所有类型都是可以的。但是List<?>并不等同于List<Object>。

       List<Object>实际上确定了List中包含的是Object及其子类,在使用的时候都可以通过Object来进行引用。

而List<?>则其中所包含的元素类型是不确定。其中可能包含的是String,也可能是 Integer。如果它包含了String的话,往里面添加Integer类型的元素就是错误的。

       正因为类型未知,就不能通过new ArrayList<?>()的方法来创建一个新的ArrayList对象。因为编译器无法知道具体的类型是什么。

       List<? extends Number>说明List中可能包含的元素类型是Number及其子类。而List<? super Number>则说明List中包含的是Number及其父类。当引入了上界之后,在使用类型的时候就可以使用上界类中定义的方法。比如访问 List<? extends Number>的时候,就可以使用Number类的intValue等方法。


       在Java中,大家比较熟悉的是通过继承机制而产生的类型体系结构。比如String继承自Object。根据原则,子类是可以替换父类的。当需要Object类的引用的时候,如果传入一个String对象是没有任何问题的。

       但是反过来的话,即用父类的引用替换子类引用的时候,就需要进行强制类型转换。编译器并不能保证运行时刻这种转换一定是合法的。这种自动的子类替换父类的类型转换机制,对于数组也是适用的。 String[]可以替换Object[]。但是泛型的引入,对于这个类型系统产生了一定的影响。正如前面提到的List<String>是不能替换掉List<Object>的。

       引入泛型之后的类型系统增加了两个维度:一个是类型参数自身的继承体系结构,另外一个是泛型类或接口自身的继承体系结构。第一个指的是对于 List<String>和List<Object>这样的情况,类型参数String是继承自Object的。而第二种指的是 List接口继承自Collection接口。对于这个类型系统,有如下的一些规则:

       相同类型参数的泛型类的关系取决于泛型类自身的继承体系结构。即List<String>是Collection<String> 的子类型,List<String>可以替换Collection<String>。这种情况也适用于带有上下界的类型声明。


泛型使用实例

1.填充数组

private static <T> void fillArray(T[] a,T obj)

 {

           for(int i=0;i<a.length;i++)

          {
                a[i] = obj;
          }
}

2.类型转换
private static <T> T autoConvert(Object obj)

{
         return (T)obj;
}
private static <T> void swap(T[] a,int i,int j)

{
        T tmp = a[i];
        a[i] = a[j];
        a[j] = tmp;
}


3.添加
private static <T> T add(T x,T y)

{
       return null;
}


4.打印集合1
public static void printCollection(Collection<?> collection)

{
        //collection.add(1);
         System.out.println(collection.size());
       for(Object obj : collection)

       {
             System.out.println(obj);
       }
}


打印集合 2
public static <T> void printCollection2(Collection<T> collection)

{
         //collection.add(1);
        System.out.println(collection.size());
        for(Object obj : collection)

       {
             System.out.println(obj);
       }
}


泛型的好处

1,类型安全。 泛型的主要目标是提高 Java 程序的类型安全。通过知道使用泛型定义的变量的类型限制,编译器可以在一个高得多的程度上验证类型假设。没有泛型,这些假设就只存在于程序员的头脑中(或者如果幸运的话,还存在于代码注释中)。

2,消除强制类型转换。 泛型的一个附带好处是,消除源代码中的许多强制类型转换。这使得代码更加可读,并且减少了出错机会。

3,潜在的性能收益。 泛型为较大的优化带来可能。在泛型的初始实现中,编译器将强制类型转换(没有泛型的话,程序员会指定这些强制类型转换)插入生成的字节码中。但是更多类型信息可用于编译器这一事实,为未来版本的 JVM 的优化带来可能。由于泛型的实现方式,支持泛型(几乎)不需要 JVM 或类文件更改。所有工作都在编译器中完成,编译器生成类似于没有泛型(和强制类型转换)时所写的代码,只是更能确保类型安全而已。



原创粉丝点击