Java泛型使用小结

来源:互联网 发布:朴槿惠犯了什么罪 知乎 编辑:程序博客网 时间:2024/05/20 10:11

1       什么是泛型

泛型,即“参数化类型”。一提到参数,最熟悉的就是定义方法时有形参,然后调用此方法时传递实参。那么参数化类型怎么理解呢?顾名思义,就是将类型由原来的具体的类型参数化,类似于方法中的变量参数,此时类型也定义成参数形式(可以称之为类型形参),然后在使用/调用时传入具体的类型(类型实参)。

可以把类型参数看作是使用参数化类型时指定的类型的一个占位符,就像方法的形式参数是运行时传递的值的占位符一样。

2       泛型使用

2.1     泛型定义

推荐你用简练的名字作为形式类型参数的名字(如果可能,单个字符),最好避免小写字母,这使它和其他的普通的形式参数很容易被区分开来。我们常见的如T、E、K、V等形式的参数常用于表示泛型形参;由于接收来自外部使用时候传入的类型实参。

 

2.2     泛型类、泛型接口、泛型方法

泛型类

泛型类的声明和非泛型类的声明类似,除了在类名后面添加了类型参数声明部分。

和泛型方法一样,泛型类的类型参数声明部分也包含一个或多个类型参数,参数间用逗号隔开。一个泛型参数,也被称为一个类型变量,是用于指定一个泛型类型名称的标识符。因为他们接受一个或多个参数,这些类被称为参数化的类或参数化的类型。

 

泛型接口

没有泛型接口时,每次试图使用一个非泛型接口来操纵一个值类型时,会丢失编译时的类型安全性。这会严重限制泛型类型的应用。一个引用类型或值类型为了实现一个泛型接口,可以具体指定类型实参;另外,一个类型也可以保持类型实参的未指定状态来实现一个泛型接口。

泛型方法

你可以写一个泛型方法,该方法在调用时可以接收不同类型的参数。根据传递给泛型方法的参数类型,编译器适当地处理每一个方法调用。

 

下面是定义泛型方法的规则:

1.所有泛型方法声明都有一个类型参数声明部分(由尖括号分隔),该类型参数声明部分在方法返回类型之前(在下面例子中的<E>);

 

2.每一个类型参数声明部分包含一个或多个类型参数,参数间用逗号隔开。一个泛型参数,也被称为一个类型变量,是用于指定一个泛型类型名称的标识符;

 

3.类型参数能被用来声明返回值类型,并且能作为泛型方法得到的实际参数类型的占位符。

泛型方法体的声明和其他方法一样。注意类型参数只能代表引用型类型,不能是原始类型(像int,double,char的等);

 

4.泛型方法使得该方法能独立于类而产生变化。以下是一个基本的指导原则:无论何时,只要你能做到,你就应该尽量使用泛型方法。也就是说,如果使用泛型方法可以取代将整个类泛型化,那么就应该只使用泛型方法,因为它可以使事情更清楚明白;

 

5.静态方法上的泛型:一个static方法,无法访问泛型类的类型参数,因为类还没有实例化,所以,若static方法需要使用泛型能力,必须使其成为泛型方法。

2.3     泛型擦除

泛型是通过java编译器的称为擦除(erasure)的前端处理来实现的。你可以(基本上就是)把它认为是一个从源码到源码的转换,它把泛型版本转换成非泛型版本。基本上,擦除去掉了所有的泛型类型信息。所有在尖括号之间的类型信息都被扔掉了这意味着它们不会添加任何的时间或者空间上的负担,这很好。不幸的是,这也意味着你不能依靠他们进行类型转换。

 

1.在于Java中的泛型这一概念提出的目的,导致其只是作用于代码编译阶段,在编译过程中,对于正确检验泛型结果后,会将泛型的相关信息擦出,也就是说,成功编译过后的class文件中是不包含任何泛型信息的;

 

2.泛型信息不会进入到运行时阶段,并且在对象进入和离开方法的边界处添加类型检查和类型转换的方法,如ArrayList<String>,编译期被编译器擦除成了ArrayList;

 

3.编译器这样做的原因是兼容性问题,兼容之前并未使用泛型的类库和代码,不得不擦除代码中有关泛型类型信息的部分,这样最后生产的代码其实是泛型无关的。

 

4.实际上,除了new表达式之外,instanceof操作和转型(会收到警告)在泛型内部都是无法使用的,而造成这个的原因就是之前讲过的编译器对类型信息进行了擦除;

 

5. 类型擦除具体指的是通过类型参数合并,将泛型类型实例关联到同一份字节码上。编译器只为泛型类型生成一份字节码,并将其实例关联到这份字节码上。

2.4     通配符

1.子类型限定,上界<? extends SomeClass>  ,用于往集合中添加元素时,既可以添加E类型对象,又可以添加E的子类型对象。为什么?因为取的时候,E类型既可以接收E类对象,又可以接收E的子类型对象;

 

2.超类型限定,下界<? super someClass>,用于当从集合中获取元素进行操作的时候,可以用当前元素的类型接收,也可以用当前元素的父类型接收;

 

<T extends Comparable<? super T>>是超类型限定的一种特殊形式,java中有大量该种用法

 

3.无限定通配符,无界<?>,用于无限定通配符适用于一些非null判断等简单操作

3       优点与不足

优点

1.类型安全。 通过知道使用泛型定义的变量的类型限制,编译器可以更有效地提高Java程序的类型安全;

 

2.消除强制类型转换。 消除源代码中的许多强制类型转换。这使得代码更加可读,并且减少了出错机会。所有的强制转换都是自动和隐式的;

 

3.提高性能。泛型为较大的优化带来可能。在泛型的初始实现中,编译器将强制类型转换(没有泛型的话,程序员会指定这些强制类型转换)插入生成的字节码中。

但是更多类型信息可用于编译器这一事实,为未来版本的 JVM 的优化带来可能。由于泛型的实现方式,支持泛型(几乎)不需要 JVM 或类文件更改。

不足

1.擦除的问题,编译时的擦除类型,实际上运行时得不到任何类型信息;

2.不能使用一些类型,如List<T>中的T不能是java中的基本数据类型;T也不能是Throwable的派生类,即不能是抛出异常的类;

3.不能同时拥有一个泛型接口的多种实现,class Test extends ArrayList<E>implements List<E>{},这种情况是无法通过编译的;

4.不能重载,一个泛型类中有一个方法void test(T t),就不能再写void  test(S s),不符合java重载的机制。同时也不能根据泛型类的类型来区分方法。

 

4       总结

1.泛型参数的限定,使用extends关键字,限定多个类型时用&隔开。如:<Textends Runnable& Serializable>

 

2.泛型参数限定中,如果限定的类型是class而不是interface,则class必须放在限定类表中的第一个,且最多只能存在一个class。如:<Textends ArrayList & Runnable& Serializable>

3.并且还要注意的一点是,Java中没有所谓的泛型数组一说

4. 关于泛型的混淆,一个常见的来源就是假设它们像数组一样是协变的。其实它们不是协变的。List<Object> 不是 List<String> 的父类型

 

0 0
原创粉丝点击