Java object casting doesn't work like I need it to

南楼画角 提交于 2019-12-12 04:46:20

问题


I'm developing native Android app on Java and I faced some unexpected behavior. Look at these classes:

public class Parent<C> {

    protected C mCallback;

    public void setCallback(Object callback) {
        mCallback = (C)callback;
    }
}

class Child<T extends Vehicle>
        extends Parent<Child.Callback<Vehicle>>{

    public void invoke(){
        mCallback.onAction(new Vehicle());
    }

    public interface Callback<T> {
        void onAction(T vehicle);
    }
}

class Vehicle {

}

And now

    Child<Vehicle> child = new Child<Vehicle>();
    child.setCallback(new Object()); // I expect ClassCastException to be thrown here!
    child.invoke(); //But it's thrown only here!

Why ClassCastException isn't thrown in .setCallback() method?

Why it is thrown only when I try to access method of Callback interface?

How to check Object callback is instance of C? Or how can I at least get ClassCastException in setCallback() method?

P.S. This is simplified example! Please consider the same, but real life question here: How to check typed callback type inside Fragment.onAttach()


One of the solutions proposed by @luckydog32 in comments here

public abstract class Parent<C> {

    protected C mCallback;

    public void setCallback(Object callback) {
        mCallback = castCallback(callback);
    }

    protected abstract C castCallback(Object callback) throws ClassCastException;
}

class Child<T extends Vehicle>
        extends Parent<Child.Callback<Vehicle>>{
    @Override
    protected Callback<Vehicle> castCallback(Object callback) throws ClassCastException {
        return (Callback<Vehicle>)callback;
    }

    public void invoke(){
        mCallback.onAction(new Vehicle());
    }

    public interface Callback<T> {
        void onAction(T vehicle);
    }
}

回答1:


This is because of something called Type Erasure. Basically after you program is compiled generic types are replaced with their Upper Bound. So for example

class Child<T extends Vehicle>

Every instance of T is just treated as if it were a Vehicle class.

So in the code:

public void setCallback(Object callback) {
    mCallback = (C)callback;
}

During run time you are pretty much saying:

public void setCallback(Object callback) {
    mCallback = (Object)callback;
}

Since you are casting callback immediately, the simple fix for your code would to just do:

public void setCallback(C callback) {
    mCallback = callback;
}



回答2:


You cannot cast to a C, as it doesn’t exist at runtime (ie compile time only) instead, use C instead of Object in your setCallback definition



来源:https://stackoverflow.com/questions/46750211/java-object-casting-doesnt-work-like-i-need-it-to

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!