0

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.

5
  • 1
    Use the instanceof keyword to determine if the runtime object is of a given subtype. Additionally, you cannot cast a superclass into a subclass type unless the object itself was initialized as the subclass type. Commented Mar 2, 2015 at 16:04
  • I know that, that's why I'm asking for a way around this :(. In my case the execute function will always work with the superclass. Maybe my question would be : How to go from a superclass to a subclass without manually copying fields? Commented Mar 2, 2015 at 16:09
  • implement cloneable? Commented Mar 2, 2015 at 16:11
  • 1
    You are asking for something that violates subtyping. You want to call new Vehicle and get a Car. The runtime is right to throw an invalid cast - a vehicle is not car; it's the other way around. Commented Mar 2, 2015 at 16:12
  • The rules of polymorphism and OOP state allow for a subtype to be stored in a superclass "container" (since a subtype is technically an instance of a supertype, just more specific) but not vice versa. Imagine for example, a list of final fields present in a given subclass populated in the constructor. If we could cast a superclass type directly to a subclass type, what would be the value of these fields (since the superclass type never invoked the subclass constructor)? Commented Mar 2, 2015 at 16:14

3 Answers 3

4

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.

Sign up to request clarification or add additional context in comments.

1 Comment

Well I think the problem here is I'm not using the proper mecanism to achieve what I want to do. I'll try other things and let you know if I find something that does the job.
1

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.

Comments

0

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.