您的位置:首页 > 编程语言 > Java开发

java Cast Exception

2016-04-11 19:40 405 查看
以下都是错误的。问题是我对运行时泛型擦除机制理解不够,太low。

Pair<Employee> pair
List<String> list = new ArrayList<String>();


这里给List设置String,能在编译阶段起到检查的功能,也就是如果如下,编译的时候会报错。

list.add(100)


但是一旦通过编译到了运行阶段,泛型String会被擦除,就没有String 之分了,只有一个单纯的List。

更进一步说,什么能够在编译阶段检查出来呢?

就如同上面,代码调用含泛型参数的接口,传入一个参数,会对这个参数的类型进行检查。但是别指望运行期间给你做什么了。

但是对于工作代码,就要谨慎了。比如方法中有一句代码是这样的:

T t =(T)obj;


那么实际的情况,这就没有作用,类型强转发生在代码运行中的。在class文件的形式就是:

Object t = (Object) obj;

记住这种实例方式

List<String> list = new ArrayList<String>();


到了Class那里就变成

List list = new ArrayList();


也就是在运行中跟String 没有半点联系。

但如果你是这样的

class MyArrayList extends ArrayList<String> {

}


那么在MyArrayList.class 中所有的泛型都会被替换为String .那么此时如果运行代码中出现

T t =(T)obj;


就是我们期望的结果了。

综上 ,这是我所碰到的问题,也是一个陷阱

public class TestOne<T> {
Listener<T> listener = null;
T str;

public void setListener(Listener<T> listener) {
this.listener = listener;
}

public void setValue(String v) {
str = (T) v;
}

public void run() {
listener.onChanged(str);
}

public static void main(String[] args) {
TestOne<String> test = new TestOne<String>();
test.setListener(new MyListener());
test.setValue("hello");
test.run();
}
}

class MyListener implements Listener<String> {
public void onChanged(String... addr) {
System.out.println("onChanged:" + addr);
}
}

interface Listener<T> {
void onChanged(T... addr);
}


TestOne是个公共类,其实现和具体的String类没有任何关联, 而其实例化过程被

TestOne<String> test = new TestOne<String>();


被蒙蔽了,在运行过程中,其内部的泛型全部替换为Object,那么在其对象中跑的数据也都是Object,所以传入到onChanged也为Object类型,这样发生Cast Exception在所难免,看class文件,在传递给onChanged函数前,会进行Object[] 强转。但是如果将数组更改为ArrayList就是ok的。:

public class TestTwo<T> {
ListenerTwo<T> listener = null;
T str;

public void setListener(ListenerTwo<T> listener) {
this.listener = listener;
}

public void setValue(String v) {
str = (T) v;
}

public void run() {
ArrayList<T> list = new ArrayList<T>();
list.add(str);
listener.onChanged(list);
}

public static void main(String[] args) {
TestTwo<String> test = new TestTwo<String>();
test.setListener(new MyListenerTwo());
test.setValue("hello");
test.run();
}
}

class MyListenerTwo implements ListenerTwo<String> {
public void onChanged(ArrayList<String> addr) {
System.out.println("onChanged:" + addr.get(0));
}
}

interface ListenerTwo<T> {
/**
* 如果使用... 就会出现[Ljava.lang.Object; cannot be cast to [Ljava.lang.String;
*/
void onChanged(ArrayList<T> addr);
}


背景:

interface A<T> {  //T是泛型
void method(T data) ;
}

class B  implement A<D> {
void method(D data)  {
...
}
}


调用:

class Caller {
E  data; // E是D的抽象类,为什么不直接用D呢,因为Caller自身是个共用类,所以E选用的是抽象类,不跟具体业务类D挂钩。
B  b;
void call(E data) {
b.method(data);//在调用这个的时候,出现cast异常。
}
}


问题:

这个和Integer及int的自动装箱完全没有关联,你将一个E对象赋给D对象,而中间没有经过强转,当然是有问题的。

如何解决:

方法一:反射

class Caller {
E  data;
B  b;
void call(E data) {
//通过反射获取得到b.method的参数类类型Class classOfD
//data经过类型强转  classOfD.cast(data)
b.method(data);
}
}


方法二:泛型

class Caller<T> {
T  data;
B  b;
void call(T data) {
b.method(data);
}
}

//实际调用时
Caller<D> caller = new Caller();


总结:

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: