Java generic to avoid ClassCastException

柔情痞子 提交于 2019-12-11 14:20:05

问题


Here is a piece of code I'm working on. I have a Callback object that can return a specified super type. Vehicle is one of these classes here. These classes are used as supertype for other classes like Car and Train here.

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

            }
        });
    }
}

Here I get ClassCastException as Vehicule cannot be directly casted to Car or Train. I would like to either call the execute giving it a type like Car or Train like so :

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
            }
        });
    }
}

I'm trying to do this because in my case, Vehicle contains a map of attribute and a Car a vehicle that has some key-value pair in that list. Train has others. My Car and Train object provides easy getter that knows the specific keys to retrieve data from the Map.

The only way I was able to do it was to remove the parent-child relation and build a decorator around a Vehicle for my Train and Car classes.


回答1:


The problem is that you have a mechanism that should work for any Vehicle, without distinguishng between Car and Train - yet you want different behaviour for Car and Train.

So there are several solutions:

1) Split up the mechanism so you have different, typed, callbacks for Car and Train

2) Use polymorphism - provide method(s) in Vehicle that you over-ride differently in Car and Train, and call these in the callbacks.

3) If all else fails, use instanceof to detect the actual type of the object before casting it to Car or Train.




回答2:


As is, you are asking for new Vehicle() to produce a Car - no compiler or cast will be able to make this work.

What you probably wanted instead is this:

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

  void onCompletion(C object);
}

where you would need to check types before calling the callback:

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

OR you could type-check in your callback using instanceof.

Javas EventListenerList does something similar, too. Again, the inner code involves ugly casting. But the API is nice, because you would register the callback with a type:

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

if I recall correctly, it stores the class and callback in an Object[] array internally, and does use casting. But this pattern is nice: register with the type.




回答3:


You can test using instanceof or YourClass.class.isInstance(OtherClass); Perhaps you could create an interface for getting your required values and test for that.



来源:https://stackoverflow.com/questions/28814115/java-generic-to-avoid-classcastexception

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