java Cast Exception

以下都是错误的。问题是我对运行时泛型擦除机制理解不够,太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();

总结:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值