一、介绍部分
定义:
java泛型是javaSE 1.5的新特性,泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。这种参数类型可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口、泛型方法和构造器(constructor。
好处:
1、类型安全。 泛型的主要目标是提高 Java 程序的类型安全,取消类型转换,减少发生错误的机会。
2、消除强制类型转换。 泛型的一个附带好处是,消除源代码中的许多强制类型转换。这使得代码更加可读,并且减少了出错机会。
3、潜在的性能收益。 泛型为较大的优化带来可能。在泛型的初始实现中,编译器将强制类型转换(没有泛型的话,程序员会指定这些强制类型转换)插入生成的字节码中。但是更多类型信息可用于编译器这一事实,为未来版本的 JVM 的优化带来可能。由于泛型的实现方式,支持泛型(几乎)不需要 JVM 或类文件更改。所有工作都在编译器中完成,编译器生成类似于没有泛型(和强制类型转换)时所写的代码,只是更能确保类型安全而已。
? 通配符,任意类型,不能用于声明泛型。
T K V E 都可以,表示普通类型。用于声明泛型,指的是某个确定类型,在实例化或者调用的时候需要指定。
普通类型或者类型加上 extends或者super构成受限类型,上限类型,下限类型。(? extends/T extends)
总结 ? extends 和 ? super 通配符的特征,我们可以得出以下结论:
- 如果你想从一个数据类型里获取数据,使用? extends 通配符
- 如果你想把对象写入一个数据结构里,使用? super 通配符
- 如果你既想存,又想取,那就别用通配符。
这就是Maurice Naftalin在他的《Java Generics and Collections》这本书中所说的存取原则,以及Joshua Bloch在他的《Effective Java》这本书中所说的PECS法则。
这PECS是指”Producer Extends, Consumer Super”,这个更容易记忆和运用。
特殊性:
Java泛型实现原理:类型擦除, Java的泛型是伪泛型。在编译期间,所有的泛型信息都会被擦除掉。正确理解泛型概念的首要前提是理解类型擦出(type erasure)。
java泛型(Generic[dʒəˈnɛrɪk] Type)只存在于java源码之中,一旦编译成字节码(.class文件),就会被替换为原始类型(Raw[rɔ] Type--裸类型),并且在相应的地方插入了强制转型代码,因此对于运行期的Java语言来说,ArrayList<int>与ArrayList<String>就是同一个类(ArrayList)。所以说泛型技术实际上是Java语言的一颗语法糖,Java语言中的泛型实现方法称为类型擦除,基于这种方法实现的泛型被称为伪泛型。
那么这就有个问题了,既然在编译的时候会在方法和类中擦除实际类型的信息,那么在返回对象时又是如何知道其具体类型的呢?如List<String>编译后会擦除掉String信息,那么在运行时通过迭代器返回List中的对象时,又是如何知道List中存储的是String类型对象呢? 擦除在方法体中移除了类型信息,所以在运行时的问题就是边界:即对象进入和离开方法的地点,这正是编译器在编译期执行类型检查并插入转型代码的地点。泛型中的所有动作都发生在边界处:对传递进来的值进行额外的编译期检查,并插入对传递出去的值的转型
Java语法糖会在后面博文中介绍学习
二、举例说明
1、根据泛型存在的位置分为:泛型类、泛型接口、泛型方法
a. 泛型类
class Point<T>{
private T var;
...
}
b.泛型接口
public interface Points<T>{
public T getValue();
}
c.泛型方法(泛型的声明,必须在方法的修饰符(public,static,final,abstract等)之后,返回值声明之前)
public static <T> void display(T t) {
System.out.println(t.getClass());
}
是否拥有泛型方法,与其所在的类是否泛型没有关系。要定义泛型方法,只需将泛型参数列表置于返回值前。
使用泛型方法时,不必指明参数类型,编译器会自己找出具体的类型。泛型方法除了定义不同,调用就像普通方法一样。
最后,需要注意的是,一个static方法,无法访问泛型类的类型参数,因为类还没有实例化,所以,若static方法需要使用泛型能力,必须使其成为泛型方法。
注意事项:
1)方法重载
在JAVA里面方法重载是不能通过返回值类型来区分的,比如代码一中一个类中定义两个如下的方法是不容许的。但是当参数为泛型类型时,却是可以的。如下面代码二中所示,虽然形参经过类型擦除后都为List类型,但是返回类型不同,这是可以的
2)泛型类型是被所有调用共享的
所有泛型类的实例都共享同一个运行时类,类型参数信息会在编译时被擦除。因此考虑如下代码,虽然ArrayList<String>和ArrayList<Integer>类型参数不同,但是他们都共享ArrayList类
3)instanceof
不能对确切的泛型类型使用instanceOf操作。如下面的操作是非法的,编译时会出错。
4)泛型数组问题
不能创建一个确切泛型类型的数组。如下面代码会出错。
List<String>[] lsa = new ArrayList<String>[10];//compile error.
5)泛型类并没有自己独有的Class类对象。
比如并不存在List<String>.class或是List<Integer>.class,而只有List.class。
6)静态变量是被泛型类的所有实例所共享的。
对于声明为MyClass<T>的类,访问其中的静态变量的方法仍然是 MyClass.myStaticVar。不管是通过new MyClass<String>还是new MyClass<Integer>创建的对象,都是共享一个静态变量。
7)泛型的类型参数不能用在Java异常处理的catch语句中。
因为异常处理是由JVM在运行时刻来进行的。由于类型信息被擦除,JVM是无法区分两个异常类型MyException<String>和MyException<Integer>的。对于JVM来说,它们都是 MyException类型的。也就无法执行与异常对应的catch语句。
思考问题:
泛型与之类型问题
比如一个方法如果接收List<Object>作为形式参数,那么如果尝试将一个List<String>的对象作为实际参数传进去,却发现无法通过编译。虽然从直觉上来说,Object是String的父类,这种类型转换应该是合理的。但是实际上这会产生隐含的类型转换问题,因此编译器直接就禁止这样的行为。
应用实例:未完待续。。。