浅谈Java泛型中的类型擦除以及编程中的典型错误

Java语言内置了泛型机制,使得我们可以使用一种集合对象便可以存储任意类型的对象,但是泛型机制也并不是能够随心所欲的使用的,很多时候我们不正确的使用可能会使我们产生疑惑。比如现在我们想要实现一个类似于ArrayList的数据结构,假设命名为MyArrayList,显而易见的,我们需要在MyArrayList中定义一个数组来存储元素,考虑到MyArrayList应该能够存储任意对象,我们应该把它设置为一个泛型类,如下的代码是显而易见的:

public class MyArrayList<T> {

}

那么,数组该怎么定义呢?是使用泛型数组,还是Object类型的数组?

private T[] datas;
private Object[] datas;

如果稍不注意,我们可能就会写成泛型数组的形式,但是很可惜,使用泛型数组的做法是错误的,查阅ArrayList源码我们可以发现,Java内部实现如下:

/**
     * The array buffer into which the elements of the ArrayList are stored.
     * The capacity of the ArrayList is the length of this array buffer. Any
     * empty ArrayList with elementData == EMPTY_ELEMENTDATA will be expanded to
     * DEFAULT_CAPACITY when the first element is added.
     */
    private transient Object[] elementData;

我们可以看到ArrayList使用Object[]数组完成数据的存储,如果使用泛型数组,那么在构造器中我们如果写下这样的代码:

datas = new T[10];

IDE会报错,报错信息为:Cannot create a generic array of T

之所以会出现这样的错误,是因为Java的泛型机制中有类型擦除的过程,而这一过程,就是上述报错信息的根源,现在我们来探讨一下类型擦除。在讨论类型擦除之前,我们有必要回顾一下数组。

在Java中,我们可以创建两种类型的数组:基本类型数组,以及自定义类型数组。这两种数组在编译期就会受到严格的类型检查,考虑以下代码:

int[] a = new int[2];
a[0] = 1;
a[1] = false;

显然这段代码是无法通过编译的,基本类型数组表现出的行为自定义类型数组同样适用,假设现在有一个自定义Shape类,并且创建一个Shape类型的数组,那么在编译时会检查该数组内的元素是否都是Shape类型的。正是因为这样的原因,下面的代码是错误的:

T[] a = new T[]; //如果T在任何地方都没有被定义

因为这样的写法编译器无法在编译时检查a数组内的元素是否属于T类型。现在回到泛型,Java的泛型是通过类型擦除机制实现的,简而言之,如果定义一个ArrayList<Shape>和一个ArrayList<Car>,Java都把它们视为Object,除了Object类的信息全部被擦除了。考虑下面一段代码:

public class A<T> {
	private T t;
	public A(T t) {
		this.t = t;
	}
	
	public void doSomething() {
		t.f();
	}
}

上述代码中定义了一个泛型类A,然后定义一个类B,B中拥有方法f();

public class B {
	public void f() {
		//dosomething....
	}
}

期望的结果是A持有一个B的对象,然后调用A的doSomething()方法,期待其自动调用B的f()方法,想法很美好,但是实际上这段代码是不能通过的,原因就是因为类型擦除,A虽然是泛型类,但是被擦除成了Object,而Object类中没有f()方法,编译器也无法确定运行时传入的类型对象是否含有f()方法(需要注意的是,如果是C++,上述代码可以是正确的)。

通过以上的分析,我们大致了解了Java泛型中的类型擦除以及带来的潜在编程问题,希望大家看完我的博客能够对泛型和数组有更深刻的了解。这些内容在《Java编程思想》中均有所涉及并且十分详尽,还有疑问可以参考。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值