如何正确使用Class.cast()?

时间:2010-09-16 05:16:18

标签: java reflection casting

我有一个如下的Command类:

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

我还有一个Command,AuthenticateCommand的子类:

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

现在假设一个类Server,它有一个方法processCommand(命令命令)。它接受命令param,检查commandName字段,并使用该名称将命令强制转换为Command的子类,负责实现命令逻辑。在此示例中,您可能拥有一个CommandName,其commandName为“authenticate”,而command和pw存储在commandArgs数组中。 processCommand()会将Command强制转换为AutheticateCommand并调用executeCommand()方法。我试图用以下内容完成此任务(commandMap只是一个将commandName映射到其实现者类名的Map):

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

对implementorClass.cast()的调用抛出了ClassCastException。它不应该以这种方式向下转发到AuthenticateCommand类吗?

更新

更多背景知识。 Server类处理的不仅仅是AuthenticateCommands。可以有任意数量的Command子类,具体取决于项目。我试图让编写客户端的人简单地传递一个只带有名称和参数的序列化Command对象。我可以强制客户端“了解”AuthenticateCommand和所有其他人,然后序列化它们并传递它们,但这似乎是次优的,因为子类之间的唯一区别是executeCommand的实现,客户端并不关心或了解。所以我只想要一种方法让客户端传递父类,并使用该父类中的数据将其转换为适当的子类。

我想我可以使用newInstance()代替cast而只是创建一个新对象,但这似乎很浪费。我想我也可以废除处理逻辑的子类的概念并将它们移动到方法中,然后processCommand将调用适当的方法。不过,这对我来说也很不舒服。

3 个答案:

答案 0 :(得分:5)

你为什么要施放?你只是试着打电话给executeCommand,这可以在Command上找到......所以只需写:

command.executeCommand();

应编译并运行。目前还不清楚地图的位置。

至于为什么演员表失败...我的 guess 是该命令的ClassLoader此时不是默认的ClassLoader,因此implementorClass是同一个类,但是由不同的ClassLoader加载......就JVM而言,这使它成为一个差异类。

编辑:我说你的设计坏了。您传递的Command对象未正确履行其角色。一种选择是拥有一个知道名称的新RemoteCommand子类,并且在调用其executeCommand方法时,它会构建适当的子类实例。是的,需要构建一个类的实例。如果没有该类的实例,则无法在类上调用实例方法,并且不能使一个对象“假装”它实际上是不同类型的对象。如果AuthenticationCommand尝试使用某些额外字段会怎样?这些价值来自哪里?

更好的选择是让你的序列化/反序列化层执行此操作,这样当你到达这段代码时,你已经得到 AuthenticationCommand - 你可以使用这个答案顶部的代码。

答案 1 :(得分:3)

你真的需要实例化它。您不能通过强制转换将Class<T>“转换”为具体实例。此外,与您的代码段相反,转换应该以相反的方式完成。

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

更不用说这一切都是一种设计气味。

答案 2 :(得分:1)

只有当Command Object在运行时实际引用Authenticate Command实例时,才能进行向下转换。这就是多态性所说的不是吗?