教你如何使用泛型(二)

泛型的基本使用可以参考我的这篇文章(教你如何使用泛型),然而,当你真正使用泛型时,还需特别小心一些陷阱。本篇文章主要为你介绍Java的泛型的类型檫除,以及类型檫除会带来哪些问题,如何正确的处理这些问题。
首先,我们先看一个例子。

    public static void main(String[] args) {
        ArrayList<String> strList = new ArrayList<String>();
        ArrayList<Integer> intList = new ArrayList<Integer>();
        System.out.println(strList.getClass());
        System.out.println(intList.getClass());
    }

我们大部分人都会认为ArrayList<String>ArrayList<Integer>是不同的类型。然而,当我们运行上述的代码时,返回的结果都是class java.util.ArrayList。为什么会出现这样的结果呢??
这是因为类型檫除的效果。

类型檫除(type erasure) : generic type information is present only at compile time, after which it is erased by the compiler.

简单的说,就是当你在使用泛型时,任何具体的信息都被檫除了。在泛型代码的内部,无法获得任何有关泛型参数类型的信息。因此,在上述代码中,ArrayList<String>ArrayList<integer>是相同的类型。
Java设计很大程度上是受C++的启发。而Java 泛型是Java SE5时才提出来的。如果你以前学过C++,那么你会发现,有些以前C++可以做到的,而Java却不能做到。为了加深对泛型的理解,我们再看一个例子。
当我们使用C++模板时,我们可以这样写。

#include <iostream>

using namespace std;

template<class T> class Manipulator{
    T obj;
public:
    Manipulator(T x){
        obj = x;
    }
    void manipulate(){
        //注意这个方法。
        obj.f();
    }
};

class HasF{
public:
    void f(){
        cout<<"call to f()";
    }
};


int main()
{
    HasF hasF;
    Manipulator<HasF> man(hasF);
    man.manipulate();
//    cout << "Hello world!" << endl;
    return 0;
}

看到上面的代码,我们可能会奇怪,obj.f().obj怎么会知道f()是为类型参数T而存在的呢?因为,当实例化模板类时,C++编译器将进行检查,在Manipulator<HasF>实例化时,它会知道HasF拥有一个f()
用C++写这种代码是很容易的,因为当模板被实例化时,模板代码知道模板参数的类型。
那么,Java泛型该如何实现呢??
如果直接像C++上面直接调用HasF中的方法f(),编译器会报错。因为类型檫除,所以无法获得泛型参数类型的信息。为了调用f()方法,我们必须协助泛型类,给定泛型类的边界,告知编译器只能接受遵循这个边界的类型。
代码如下。
HasF.java

package genericdemo.typeerasure;

public class HasF {
    void f(){
        System.out.println("call to f()");
    }
}

Manipulator.java

package genericdemo.typeerasure;

import java.util.ArrayList;

public class Manipulator<T extends HasF> {
    private T obj;
    public Manipulator(T x) {
        obj = x;
    }
    public void method(){
        obj.f();
    }

    public static void main(String[] args) {
        ArrayList<String> strList = new ArrayList<String>();
        ArrayList<Integer> intList = new ArrayList<Integer>();
        System.out.println(strList.getClass());
        System.out.println(intList.getClass());
        System.out.println(strList.getClass() == intList.getClass());
        HasF hasF = new HasF();
        Manipulator<HasF> man = new Manipulator<HasF>(hasF);
        man.method();
    }
}

可能你会问,既然类型擦除会带来种种问题,那么,为什么还要使用擦除呢??首先,你要明白,Java一开始是不支持泛型的。泛型是Java SE5所作出的重大改变。如果从Java 1.0时,就支持泛型,那么设计者肯定不会使用擦除。因此,之所以使用擦除,是在不破坏现有类库的情况下,将泛型融入Java语言。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值