Java 泛型机制详解(1)-简单泛型使用

系列文章链接

 

一、为什么要使用泛型

其实新技术的出现都是原有的技术不能够满足需求了,或者是原有技术有比较大的弊端,大家都用的不舒服,那泛型也是这样的。

老生常谈的ArrayList  在没有泛型之前是这样的

public class ArrayList 
{
    private Object[] elementData;
    public Object get(int i) { . . , }
    public void add(Object o) { . . . }
}

这样的设计主要有两个问题,第一是获取值得时候需要强制类型转换,容易出错,不能直接使用object吧,那也没啥可用的;第二是数组类型为object意味着无论什么类型都能放到ArrayList里面,那取出的时候可能不知道原本是什么类型的对象了。

 

现在是这样的

public class ArrayList<E>
{
    transient Object[] elementData;
    public E get(int index) {...};
    public boolean add(E e) {...};
}

其实就是多了个E,这个E就是一种泛型,泛型可以理解为是对引用数据类型的一种抽象,代表现在我ArrayList不是什么都接收,我只接收类型为E的(子类的问题后面谈)

可以看到加入了泛型之后,list不能插入Person类型之外的类型了,这样第二个问题就算解决了。

要解答第一个问题,首先要知道java程序执行的三个阶段,这里不多介绍;要知道的就是泛型E在经过反编译成class文件(这里的class文件代码只是表达这么个意思,实际的不长这样)后,泛型被转换成Object了,这意味着程序运行的时候根本没有泛型这个概念,泛型只存在于程序员的眼中,

public static void main(String[] args) {
        List<Person> list = new ArrayList<>();
        list.add(new Person("张三"));
        Person person = list.get(0);
    }

那如果从class文件的角度看,list.get(0)得到的不就是Object对象了吗?实际上在class文件中这句应该是Person person = (Person)list.get(0);编译器会帮助我们做强制类型转换,这样不用人为转换,也就降低了出错的可能性。

二、创建一个简单的泛型类

public class ShowName <T>{
    private T first;
    private T second;

    public ShowName() {
    }

    public ShowName(T first, T second) {
        this.first = first;
        this.second = second;
    }

    public T getFirst() {
        return first;
    }

    public void setFirst(T first) {
        this.first = first;
    }

    public T getSecond() {
        return second;
    }

    public void setSecond(T second) {
        this.second = second;
    }
}
public static void main(String[] args) {
        ShowName<Person> showName = new ShowName<>();
        showName.setFirst(new Person("first"));
        showName.setSecond(new Person("second"));
        System.out.println("第一个值:" + showName.getFirst() + "\n" + "第二个值:" + showName.getSecond() + "\n");
    }

ShowName类引入了一个类型变量T,当然也可以以引入多个比如public class ShowName <K,V>,类型变量或者说类型参数可以在这个泛型类中域、局部变量和方法的返回类型中使用,泛型方法除外

例子中我用的是Person类,其实也可以用String Integer这种类型,对应的set和get方法也必须和使用的类型匹配。这也是泛型最基本的作用。

注释:类型变量使用大写形式,且比较短,这是很常见的。在java库中,使用变量E表示集合的元素类型,K和V分别表示表的关键字与值的类型,T(需要时还可以用U和S)表示任意类型。

三、泛型方法

泛型方法和泛型类是相似的,只是作用域不同,一个是作用于类,整个类可以使用定义的泛型,一个是只作用于定义的泛型方法内部,可以泛型类中有泛型方法,但是泛型方法的作用域会覆盖泛型类,也可以泛型方法在普通类之中。

我们可以看一下ArrayList的toArray方法

public <T> T[] toArray(T[] a) {
        if (a.length < size)
            // Make a new array of a's runtime type, but my contents:
            return (T[]) Arrays.copyOf(elementData, size, a.getClass());
        System.arraycopy(elementData, 0, a, 0, size);
        if (a.length > size)
            a[size] = null;
        return a;
    }

对于泛型方法,类型变量是放在修饰符后面,返回参数前面,很明显这个泛型方法中的T和ArrayList<E>中的E没有什么关联。

泛型方法一般来说在调用的时候,不用标明T的类型,编译器可以通过参数和返回值来判断T的具体类型,但是如果是多个参数输入,比如add(T ...a)输入 add(1,1.01,2)这种,那编译器会查找参数共同的父类,比如Number,或者是给出报错提示。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值