views:

76

answers:

3

Hey all,

I have a Command class like the following:

public class Command {
    ...
    private String commandName;
    private Object[] commandArgs;
    ...
    public void executeCommand() {}
}

I also have a subclass of Command, AuthenticateCommand:

public class AuthenticateCommand extends Command {
    ...
    @Override
    public void executeCommand() {
        ...
    }
}

Now imagine a class, Server, that has a method processCommand(Command command). It takes the command param, inspects the commandName field, and uses that name to cast the command to a subclass of Command responsible for implementing the command logic. In this example, you might have a Command with a commandName of "authenticate" and the username and pw stored in the commandArgs array. processCommand() would cast the Command to AutheticateCommand and invoke the executeCommand() method. I'm trying to accomplish this with the following (commandMap is just a Map that maps a commandName to its implementor class name):

public void processCommand(Command command) {
    String commandName = command.getCommandName();
    String implementorClassString = commandMap.get(commandName);
    try {
        Class implementorClass = Class.forName(implementorClassString);
        Object implementor = implementorClass.cast(command);
        Method method = implementorClass.getDeclaredMethod("executeCommand", null);
        method.invoke(implementor);
    } catch (ClassNotFoundException e) {
        logger.error("Could not find implementor class: " + implementorClassString, e);
    } catch (NoSuchMethodException e) {
        logger.error("Could not find executeCommand method on implementor class: " + implementorClassString, e);
    } catch (IllegalAccessException e) {
        logger.error("Could not access private member/method on implementor class: " + implementorClassString, e);
    } catch (InvocationTargetException e) {
        logger.error("Could not invoke executeCommand method on implementor class: " + implementorClassString, e);
    }
}

The call to implementorClass.cast() is throwing a ClassCastException. Shouldn't it be able to downcast to the AuthenticateCommand class in this manner?

UPDATE

Some more background. The Server class handles more than just AuthenticateCommands. There could be any number of Command subclasses, depending on the project. I'm trying to make it simple for someone writing a Client to pass a serialized Command object with just a name and arguments. I could force the client to "know about" AuthenticateCommand and all the others, and then serialize those and pass them, but that seems sub-optimal because the only difference between the subclasses is the implementation of executeCommand, which the client doesn't care or know about. So I just want a way to have the Client pass the parent class, and use data within that parent class to cast it to the appropriate subclass.

I suppose I could use newInstance() instead of cast and just create a new object, but that seems wasteful. I suppose I could also do away with the concept of subclasses handling the logic and move those into methods, and then processCommand would call the appropriate method. That feels janky to me as well, though.

+5  A: 

Why are you casting at all? You're just trying to call executeCommand, and that's available on Command... so just write:

command.executeCommand();

which should compile and run. It's not clear where the map comes in at all.

As for why the cast is failing... my guess is that the ClassLoader for the command isn't the default ClassLoader at this point, so that implementorClass is the same class, but loaded by a different ClassLoader... which makes it a difference class as far as the JVM is concerned.

EDIT: I'd say your design is broken. The Command object you're being passed isn't fulfilling its role properly. One option would be to have a new RemoteCommand subclass which knows the name, and when its executeCommand method is called, it builds the appropriate subclass instance. And yes, it will need to build an instance of the class. You can't call an instance method on a class without an instance of that class, and you can't make one object "pretend" that it's actually an object of a different type. What if AuthenticationCommand has some extra fields it tries to use? Where would the values come from?

A nicer alternative is to make your serialization/deserialization layer do this, so that by the time you've reached this bit of code, you've already got an AuthenticationCommand - and you can use the code at the top of this answer.

Jon Skeet
I think you didn't notice. He is trying to cast Command to AuthenticateCommand based on commandName passed. So Method actually receives Command Object and not AuthenticateCommand.
YoK
Jon's right, this is just polymorphism, or rather lack of understanding of how it works.
Jon Freedman
@YoK - the cast (as presented here) is not nessecary, Richard casts the Command instance to the implenting class type but store the result in an `Object` variable...
Andreas_D
See my updates. When processCommand is called, it receives an instance of the parent Command class, not a subclass. So if I just call command.executeCommand(), it will call the parent class's method, which does nothing.
Richard Handworker
@Richard: Editing now...
Jon Skeet
+1  A: 

You would be able to downcast only when Command Object actually references Authenticate Command instance at runtime. This is what polymorphism talks about isnt it?

hakish
+1  A: 

You really need to instantiate it. You can't "convert" a Class<T> to a concrete instance by just casting. Also, the casting should be done the other way round as opposed to your code snippet.

Class<?> implementorClass = Class.forName(implementorClassString);
Command instance = Command.class.cast(implementorClass.newInstance());
instance.executeCommand();

Not to mention that this all is a design smell.

BalusC