在使用泛型类、方法和接口时,有一些值得注意的地方,比如:
- 基本类型不能用于实例化类型参数。
- 运行时类型信息不适用于泛型。
- 类型擦除可能会引发一些冲突。
我们逐个来看下。Java中,因为类型参数会被替换为Object,所以Java泛型中不能使用基本数据类型,也就是说,类似下面的写法是不合法的:
Pair<int> minmax = new Pair<int>(1,100);
解决方法是使用基本类型对应的包装类。
在介绍继承的实现原理时,我们提到在内存中每个类都有一份类型信息,而每个对象也都保存着其对应类型信息的引用。在Java中,这个类型信息也是一个对象,它的类型为Class, Class本身也是一个泛型类,每个类的类型对象可以通过<类名>.class
的方式引用,比如String. class
、Integer.class
。这个类型对象也可以通过对象的getClass()方法获得,比如:
Class<? > cls = "hello".getClass();
这个类型对象只有一份,与泛型无关,所以Java不支持类似如下写法:
Pair<Integer>.class
一个泛型对象的getClass方法的返回值与原始类型对象也是相同的,比如,下面代码的输出都是true:
Pair<Integer> p1 = new Pair<Integer>(1,100);
Pair<String> p2 = new Pair<String>("hello", "world");
System.out.println(Pair.class==p1.getClass()); //true
System.out.println(Pair.class==p2.getClass()); //true
之前,我们介绍过instanceof关键字,instanceof后面是接口或类名,instanceof是运行时判断,也与泛型无关,所以,Java也不支持类似如下写法:
if(p1 instanceof Pair<Integer>)
不过,Java支持如下写法:
if(p1 instanceof Pair<? >)
由于类型擦除,可能会引发一些编译冲突,这些冲突初看上去并不容易理解,我们通过一些例子介绍。前面我们介绍过一个例子,有两个类Base和Child, Base的声明为:
class Base implements Comparable<Base>
Child的声明为:
class Child extends Base
Child没有专门实现Comparable接口,前面我们说Base类已经有了比较所需的全部信息,所以Child没有必要实现,可是如果Child希望自定义这个比较方法呢?直觉上,可以这样修改Child类:
class Child extends Base implements Comparable<Child>{
//主体代码
}
遗憾的是,Java编译器会提示错误,Comparable接口不能被实现两次,且两次实现的类型参数还不同,一次是Comparable<Base>
,一次是Comparable<Child>
。为什么不允许呢?因为类型擦除后,实际上只能有一个。
那Child有什么办法修改比较方法呢?只能是重写Base类的实现,如下所示:
class Child extends Base {
@Override
public int compareTo(Base o) {
if(! (o instanceof Child)){
throw new IllegalArgumentException();
}
Child c = (Child)o;
//比较代码
return 0;
}
//其他代码
}
另外,你可能认为可以如下定义重载方法:
public static void test(DynamicArray<Integer> intArr)
public static void test(DynamicArray<String> strArr)
虽然参数都是DynamicArray,但实例化类型不同,一个是DynamicArray<Integer>
,另一个是DynamicArray<String>
,同样,遗憾的是,Java不允许这种写法,理由同样是类型擦除后它们的声明是一样的。
酷客教程相关文章:
评论前必须登录!
注册