Java泛型以避免ClassCastException

时间:2015-03-02 15:59:40

标签: java generics classcastexception

这是我正在处理的一段代码。我有一个可以返回指定超类型的Callback对象。 Vehicle是这些课程中的一个。这些类在此处用作CarTrain等其他类的超类型。

interface CompletionCallBack<Result> {
    void onCompletion(Result result);
}

class Vehicle {
    Map<String, Object> attributes;
}

class Car extends Vehicle {
    public String getMake(){
        return attributes.get("make").toString();
    }
}

class Train extends Vehicle {
    public String getMake(){
        return attributes.get("size").toString();
    }
}

public class Main {
    static void execute(CompletionCallBack<Vehicle> callBack) {
        callBack.onCompletion(new Vehicle());
    }

    public static void main(String[] args) {
        execute(new CompletionCallBack<Vehicle>() {
            @Override
            public void onCompletion(Vehicle result) {
                Car car = (Car) result; //Exception
            }
        });
        execute(new CompletionCallBack<Vehicle>() {
            @Override
            public void onCompletion(Vehicle result) {
                Train train = (Train) result; //Exception

            }
        });
    }
}

这里我得到ClassCastException,因为Vehicule无法直接转换为Car或Train。我想致电execute给它类似CarTrain这样的类型:

public class Main {

    static void executeGeneric(CompletionCallBack<? extends Vehicle> callBack) {
        callBack.onCompletion(new Vehicle()); //compilation error
    }

    public static void main(String[] args) {
        executeGeneric(new CompletionCallBack<Car>() {
            @Override
            public void onCompletion(Car car) {
                //I receive a car here
            }
        });
    }
}

我正在尝试这样做,因为在我的情况下,Vehicle包含属性地图和Car车辆,该车辆在该列表中具有一些键值对。火车有其他人。我的CarTrain对象提供了一个简单的getter,它知道从Map检索数据的特定键。

我能够做到的唯一方法是删除父子关系,并为VehicleTrain类的Car周围构建一个装饰器。

3 个答案:

答案 0 :(得分:4)

问题是你有一个适用于任何车辆的机制,而不区分汽车和火车 - 但你想要汽车和火车的不同行为。

所以有几种解决方案:

1)拆分机制,以便为Car and Train提供不同的,类型化的回调

2)使用多态 - 在Vehicle中提供方法,在Car和Train中以不同的方式覆盖,并在回调中调用它们。

3)如果所有其他方法都失败了,请使用instanceof检测对象的实际类型,然后再将其投放到Car或Train。

答案 1 :(得分:1)

原样,你要求new Vehicle()生成一个Car - 没有编译器或演员可以使这个工作。

你可能想要的是:

interface CompletionCallback<C> {
  Class<C> getType();

  void onCompletion(C object);
}

您需要在调用回调之前检查类型:

for (CompletionCallback<?> c : callBacks) {
  if (c.getType().isAssignableFrom(vehicle)) {
    // Perform some ugly casting here
  }
}

或者您可以使用instanceof键入检入回调。

Javas EventListenerList也做了类似的事情。同样,内部代码涉及丑陋的铸造。但API很不错,因为你会用类型注册回调:

addCallback(Car.class, new Callback<Car>() {...});
addCallback(Submarine.class, new Callback<Submarine>() { ... });

如果我没记错的话,它会在内部将类和回调存储在Object[]数组中,并使用强制转换。但这种模式很好:注册类型。

答案 2 :(得分:0)

您可以使用instanceofYourClass.class.isInstance(OtherClass);进行测试。也许您可以创建一个界面来获取所需的值并对其进行测试。