Java 使用泛型类、方法和接口

在使用泛型类、方法和接口时,有一些值得注意的地方,比如:

  • 基本类型不能用于实例化类型参数。
  • 运行时类型信息不适用于泛型。
  • 类型擦除可能会引发一些冲突。

我们逐个来看下。Java中,因为类型参数会被替换为Object,所以Java泛型中不能使用基本数据类型,也就是说,类似下面的写法是不合法的:

Pair<int> minmax = new Pair<int>(1,100);

解决方法是使用基本类型对应的包装类。

在介绍继承的实现原理时,我们提到在内存中每个类都有一份类型信息,而每个对象也都保存着其对应类型信息的引用。在Java中,这个类型信息也是一个对象,它的类型为Class, Class本身也是一个泛型类,每个类的类型对象可以通过<类名>.class的方式引用,比如String. classInteger.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不允许这种写法,理由同样是类型擦除后它们的声明是一样的。

酷客教程相关文章:

赞(0)

评论 抢沙发

评论前必须登录!