Java对象转换不像我需要的那样工作

时间:2017-10-14 23:13:02

标签: java android inheritance parameterized-types

我正在开发Java上的原生Android应用程序,我遇到了一些意想不到的行为。看看这些课程:

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 {

}

现在

    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!

为什么不在.setCallback()方法中抛出ClassCastException?

为什么只有在我尝试访问Callback接口的方法时才抛出它?

如何检查对象回调是C的实例?或者我怎样才能在setCallback()方法中至少获得ClassCastException?

P.S。这是一个简化的例子!请在此处考虑相同但真实的问题:How to check typed callback type inside Fragment.onAttach()

@ luckydog32在评论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);
    }
}

2 个答案:

答案 0 :(得分:3)

你不能转换为C,因为它在运行时不存在(即仅编译时),而是在setCallback定义中使用C而不是Object

答案 1 :(得分:2)

这是因为名为Type Erasure的东西。基本上在编译程序之后,泛型类型将被其上限替换。例如,

if (requestCode == WRITE_REQUEST_CODE && resultCode == Activity.RESULT_OK) {
    Uri uri = resultData.getData();

    try {
        OutputStream os=getContentResolver().openOutputStream(uri);
        PrintStream ps=new PrintStream(os);
        ps.print("Message to write");
        ps.flush();
        ps.close();
    } catch (Exception e) {
        Log.e("Some identifying string", "Exception writing to "+uri.toString(), e);
    }
}

T的每个实例都被视为是一个Vehicle类。

所以在代码中:

class Child<T extends Vehicle>

在运行期间你几乎都说:

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

由于您正在立即进行回调,因此对代码的简单修复就是:

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