Java泛型 类型擦除在继承中引入的问题及编译器的解决方法

举例来说明类型擦除在继承中引入的问题以及编译器是如何解决的。

有一个Pair类:

package generic;

/**
 * @version 1.00 2004-05-10
 * @author Cay Horstmann
 */
public class Pair<T> 
{
   private T first;
   private T second;

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

   public T getFirst() { return first; }
   public T getSecond() { return second; }

   public void setFirst(T newValue) { first = newValue; }
   public void setSecond(T newValue) { second = newValue; }
}

Pair类类型擦除后变成这样:

package generic;

/**
 * pair<T>类型擦出后的版本,即pair<T>得原始类型;
 * 擦除规则是,删除类名后面的类型变量,并将类中用到
 * 类型变量的地方替换为类型变量的限定类型(
 * 如果没有限定类型就用Object)
 * @author yuncong
 *
 */
public class Pair {

	   private Object first;
	   private Object second;

	   public Pair() { first = null; second = null; }
	   public Pair(Object first, Object second) { this.first = first;  this.second = second; }

	   public Object getFirst() { return first; }
	   public Object getSecond() { return second; }

	   public void setFirst(Object newValue) { first = newValue; }
	   public void setSecond(Object newValue) { second = newValue; }
}

DateInterval继承自Pair<Date>,它表示一个时间区间,源码如下:

package generic;

import java.util.Date;

public class DateInterval extends Pair<Date>{
	public void setSecond(Date second) {
		if (second.compareTo(getFirst()) >= 0) {
			super.setSecond(second);
		}
	}

}

DateInterval类型擦除后的样子如下:

package generic;

import java.util.Date;

public class DateInterval extends Pair{
	public void setSecond(Date second) {
		if (second.compareTo(getFirst()) >= 0) {
			super.setSecond(second);
		}
	}

}

下面是一个测试类:

package generic;

import java.util.Date;

public class Test7 {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		// date2大于date1
		Date date1 = new Date();
		try {
			Thread.sleep(3000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		Date date2 = new Date();
		DateInterval dateInterval = new DateInterval();
		Pair<Date> datePair = dateInterval;
		datePair.setFirst(date1);
		datePair.setSecond(date2);
		System.out.println(datePair.getFirst());
		System.out.println(datePair.getSecond());

	}

}

运行结果如下:

Tue Jun 09 10:25:51 CST 2015
Tue Jun 09 10:25:54 CST 2015

改动一下测试类,把first设置为date2,把second设置为date1:

package generic;

import java.util.Date;

public class Test7 {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		// date2大于date1
		Date date1 = new Date();
		try {
			Thread.sleep(3000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		Date date2 = new Date();
		DateInterval dateInterval = new DateInterval();
		Pair<Date> datePair = dateInterval;
		datePair.setFirst(date2);
		datePair.setSecond(date1);
		System.out.println(datePair.getFirst());
		System.out.println(datePair.getSecond());

	}

}

运行结果如下:
Tue Jun 09 10:27:15 CST 2015
null

因为date1小于getFirst()的返回值date2,所以second并没有成功赋值为date1。可见,代码正常运行,

datePair.setSecond(date1);

执行的是DateInterval的setSecond(Date second)方法。

(问题1)

但这和我理解的不太一样。因为类型擦除后,Pair中的setSecond方法如下:

public void setSecond(Object newValue) { second = newValue; }
DateIntervalsetSecond方法如下:

public void setSecond(Date second) {
		if (second.compareTo(getFirst()) >= 0) {
			super.setSecond(second);
		}
<span style="font-size:24px;">	}</span>
按照动态绑定的规则,

datePair.setSecond(date1);

执行的应该是Pari的setSecond(Object newValue)方法,因为这个方法并没有在DateInterval被重写。

(问题1解决)

原来,编译器在DateInterval中添加了这样一个桥方法:

	public void setSecond(Object second) {
		setSecond((Date) second);
	}

这样,就可以理解为什么上面的代码能够正常执行了。

(问题2)

这里又出现了另一个问题,如果在DateInterval中也重写了getSecond方法:

	public Date getSecond() {
		return (Date) super.getSecond().clone();
	}

为了让 DateIntervalgetSecond方法与Pair中的getSecond方法一致,编译器同样会在DateInterval中添加了这样一个桥方法:

	public Object getSecond() {
		return getSecond();
	}

这样 DateInterval就有两个方法签名一样的getSecond方法,这是不符合Java编码规则的。

(问题2解决)

原来虚拟机通过方法签名和返回类型确定一个方法,因此,编译器可以产生方法签名相同但返回类型不同的方法字节码。这是《Java核心技术 卷1》中给出的解答。

(关于问题2的解决,我有疑问)

Pair和DateInterval的getSecond方法有相同的方法签名,它俩的动态绑定执行能够顺序进行,为什么还要添加一个桥方法呢?(Java泛型类继承时,子类的类型变量要么和父类一样,要么是父类的子类,也就是说DateInterval的getSecond方法的返回类型要么是Object要么是Object的子类型,它们符合“具有协变的返回类型”)。希望知道答案的朋友能给在评论中给出答案。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值