真正搞定泛型!!!------- >类型形参
所谓泛型,就是允许在定义类、接口、方法时使用类型形参,这个类型形参(或叫泛型)将在声明变量、创建对象、调用方法时动态地指定(即传入实际的类型参数,也可称为类型实参)。Java 5改写了集合框架中的全部接口和类,为这些接口、类增加了泛型支持,从而可以在声明集合变量、创建集合对象时传入类型实参,这就是在前面程序中看到的List和ArrayList两种类型。
上面三个接口声明是比较简单的,除了尖括号中的内容—这就是泛型的实质:允许在定义接口、类时声明泛型形参,泛型形参在整个接口、类体内可当成类型使用,几乎所有可使用普通类型的地方都可以使用这种泛型形参。
注意事项:
Set形式是一种特殊的数据类型,是一种与Set不同的数据类型—可以认为是Set类型的子类。
具体应用的时候:如使用List类型时,如果为E形参传入String类型实参,则产生 了一个新的类型:List类型,可以把List想象成E被全部替换成String的特殊List子接口。但实际上,系统并没有为ArrayList生成新的class文件。
通过这种方式,就解决了一个问题问题:虽然程序只定义了一 个List接口,但实际使用时可以产生无数多个List接口,只要为E传入不同的类型实参,系统就会多出一个新的List子接口。
必须指出:
List绝不会被替换成ListString,系统没有进行源代码复制,二进制代码中没有,磁盘中没有,内存中也没有。
注意:
包含泛型声明的类型可以在定义变量、创建对象时传入一个类型实参,从而可以动态地生成无数多个逻辑上的子类,但这种子类在物理上并不存在。可以为任何类、接口增加泛型声明(并不是只有集合类才可以使用泛型声明,虽然集合类是泛型的重要使用场所)。下面自定义一个Apple类,这个Apple类就可以包含一个泛型声明。构造器名不能加泛型!!!
上面程序定义了一个带泛型声明的Apple类(不要理会这个泛型形参是否具有实际意义),使用Apple类时就可为T形参传入实际类型,这样就可以生成如Apple、Apple等形式的多个子类(物理上并不存在),这就是可以使用List、ArrayList等类型的原因—JDK在定义List、ArrayList等接口、类时使用了泛型声明,所以在使用这些类时为之传入了实际的类型参数。
注意:
当创建带泛型声明的自定义类,为该类定义构造器时,构造器名还是原来的类名,不要增加泛型声明。例如,为Apple类定义构造器,其构造器名依然是Apple,而不是Apple!调用该构造器 时却可以使用Apple的形式,当然应该为T形参传入实际的类型参
数。Java 7提供了“菱形”语法,允许省略<>中的类型实参。
那么问题来了,用泛型定义了的类和接口,派生子类和普通类派生子类有何不同???
当创建了带泛型声明的接口、父类之后,可以为该接口创建实现类,或从该父类派生子类,需要指出的是,当使用这些接口、父类时不能再包含泛型形参。例如,下面代码就是错误的。
方法中的形参代表变量、常量、表达式等数据,称为形参,或者称为数据形参。定义方法时可以声明数据形参,调用方法(使用方法)时必须为这些数据形参传入实际的数据;与此类似的是,定义类、接口、方法时可以声明泛型形参,使用类、接口、方法时应该为泛型形参传入实际的类型。如果想从Apple类派生一个子类,则可以改为如下代码:
调用方法时必须为所有的数据形参传入参数值,与调用方法不同的是,使用类、接口时也可以不为泛型形参传入实际的类型参数,即下面代码也是正确的。
像这种使用Apple类时省略泛型的形式被称为原始类型(raw type)。
如果从Apple类派生子类,则在Apple类中所有使用T类型的地方都将被替换成String类型,即它的子类将会继承到String getInfo()和void setInfo(String info)两个方法,如果子类需要重写父类的方法,就必须注意这一点。
如果使用Apple类时没有传入实际的类型(即使用原始类型),Java编译器可能发出警告:使用了未经检查或不安全的操作—这就是泛型检查的警告,读者在前一章中应该多次看到这样的警告。此时,系统会把Apple类里的T形参当成Object类型处理
创建带泛型声明的接口的实现类与此几乎完全一样。
疑问:那么是不是就是存在泛型类呢?
回到:并不存在泛型类。
前面提到可以把ArrayList类当成ArrayList的子类,事实上,ArrayList类也确实像一种特殊的ArrayList类:该ArrayList对象只能添加String对象作为集合元素。但实际上,系统并没有为ArrayList生成新的class文件,而且也不会把ArrayList当成新类来处理。
因为不管泛型的实际类型参数是什么,它们在运行时总有同样的类(class)。
那么反过来讲:
也就是说不管为泛型形参传入哪一种类型实参,对于Java来说,它们依然被当成同一个类处理,在内存中也只占用一块内存空间,因此在静态方法、静态初始化块或者静态变量(它们都是类相关的)的声明和初始化中不允许使用泛型形参。
同时:由于系统中并不会真正生成泛型类,所以instanceof运算符后不能使用泛型类。