java 泛型详解( 二 )

 
12-27 09:20:04.432 13063-13063/? D/泛型测试: key is 12345612-27 09:20:04.432 13063-13063/? D/泛型测试: key is key_vlaue定义的泛型类,就一定要传入泛型类型实参么?并不是这样,在使用泛型的时候如果传入泛型实参,则会根据传入的泛型实参做相应的限制,此时泛型才会起到本应起到的限制作用 。如果不传入泛型类型实参的话,在泛型类中使用泛型的方法或成员变量定义的类型可以为任何的类型 。
看一个例子:
Generic generic = new Generic("111111");Generic generic1 = new Generic(4444);Generic generic2 = new Generic(55.55);Generic generic3 = new Generic(false);Log.d("泛型测试","key is " + generic.getKey());Log.d("泛型测试","key is " + generic1.getKey());Log.d("泛型测试","key is " + generic2.getKey());Log.d("泛型测试","key is " + generic3.getKey());D/泛型测试: key is 111111D/泛型测试: key is 4444D/泛型测试: key is 55.55D/泛型测试: key is false 
注意:

  • 泛型的类型参数只能是类类型,不能是简单类型 。
  • 不能对确切的泛型类型使用instanceof操作 。如下面的操作是非法的,编译时会出错 。
if(ex_num instanceof Generic<Number>){ }
4.4 泛型接口泛型接口与泛型类的定义及使用基本相同 。泛型接口常被用在各种类的生产器中,可以看一个例子:
//定义一个泛型接口public interface Generator<T> {public T next();}当实现泛型接口的类,未传入泛型实参时:
/** * 未传入泛型实参时,与泛型类的定义相同,在声明类的时候,需将泛型的声明也一起加到类中 * 即:class FruitGenerator<T> implements Generator<T>{ * 如果不声明泛型,如:class FruitGenerator implements Generator<T>,编译器会报错:"Unknown class" */class FruitGenerator<T> implements Generator<T>{@Overridepublic T next() {return null;}}当实现泛型接口的类,传入泛型实参时:
/** * 传入泛型实参时: * 定义一个生产器实现这个接口,虽然我们只创建了一个泛型接口Generator<T> * 但是我们可以为T传入无数个实参,形成无数种类型的Generator接口 。* 在实现类实现泛型接口时,如已将泛型类型传入实参类型,则所有使用泛型的地方都要替换成传入的实参类型 * 即:Generator<T>,public T next();中的的T都要替换成传入的String类型 。*/public class FruitGenerator implements Generator<String> {private String[] fruits = new String[]{"Apple", "Banana", "Pear"};@Overridepublic String next() {Random rand = new Random();return fruits[rand.nextInt(3)];}}4.5 泛型通配符我们知道Ingeter是Number的一个子类,同时在特性章节中我们也验证过Generic<Ingeter>与Generic<Number>实际上是相同的一种基本类型 。那么问题来了,在使用Generic<Number>作为形参的方法中,能否使用Generic<Ingeter>的实例传入呢?在逻辑上类似于Generic<Number>和Generic<Ingeter>是否可以看成具有父子关系的泛型类型呢?
为了弄清楚这个问题,我们使用Generic<T>这个泛型类继续看下面的例子:
public void showKeyValue1(Generic<Number> obj){Log.d("泛型测试","key value is " + obj.getKey());} 
Generic<Integer> gInteger = new Generic<Integer>(123);Generic<Number> gNumber = new Generic<Number>(456);showKeyValue(gNumber);// showKeyValue这个方法编译器会为我们报错:Generic<java.lang.Integer> // cannot be applied to Generic<java.lang.Number>// showKeyValue(gInteger); 
通过提示信息我们可以看到Generic<Integer>不能被看作为`Generic<Number>的子类 。由此可以看出:同一种泛型可以对应多个版本(因为参数类型是不确定的),不同版本的泛型类实例是不兼容的 。
回到上面的例子,如何解决上面的问题?总不能为了定义一个新的方法来处理Generic<Integer>类型的类,这显然与java中的多台理念相违背 。因此我们需要一个在逻辑上可以表示同时是Generic<Integer>和Generic<Number>父类的引用类型 。由此类型通配符应运而生 。
我们可以将上面的方法改一下:
public void showKeyValue1(Generic<?> obj){Log.d("泛型测试","key value is " + obj.getKey());}类型通配符一般是使用?代替具体的类型实参,注意了,此处’?’是类型实参,而不是类型形参。重要说三遍!此处’?’是类型实参,而不是类型形参 ! 此处’?’是类型实参,而不是类型形参 !再直白点的意思就是,此处的?和Number、String、Integer一样都是一种实际的类型,可以把?看成所有类型的父类 。是一种真实的类型 。


推荐阅读