Как правильно использовать Class.cast ()? - PullRequest
2 голосов
/ 16 сентября 2010

У меня есть класс 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 (команда command). Он принимает команду param, проверяет поле commandName и использует это имя для приведения команды к подклассу Command, отвечающему за реализацию логики команды. В этом примере вы можете использовать команду с именем команды «authenticate» и именем пользователя и pw, хранящимися в массиве commandArgs. processCommand () приведёт команду к AutheticateCommand и вызовет метод executeCommand (). Я пытаюсь сделать это с помощью следующего (commandMap - это просто карта, которая отображает имя_команды на имя класса разработчика):

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 таким образом?

UPDATE

Еще немного фона. Класс Server обрабатывает больше, чем просто AuthenticateCommands. В зависимости от проекта может быть любое количество подклассов Command. Я пытаюсь упростить кому-то, пишущему Клиент, передавать сериализованный объект Command только с именем и аргументами. Я мог бы заставить клиента «знать» об AuthenticateCommand и всех остальных, а затем сериализовать их и передать их, но это кажется неоптимальным, потому что единственное различие между подклассами - реализация executeCommand, которая не заботит клиента или знать о. Поэтому я просто хочу, чтобы Клиент передавал родительский класс и использовал данные в этом родительском классе для приведения его к соответствующему подклассу.

Полагаю, я мог бы использовать newInstance () вместо приведения и просто создать новый объект, но это кажется расточительным. Я полагаю, что мог бы также отказаться от концепции подклассов, обрабатывающих логику, и переместить их в методы, а затем processCommand вызовет соответствующий метод. Хотя и это мне не по зубам.

Ответы [ 3 ]

5 голосов
/ 16 сентября 2010

Почему ты вообще кастуешься?Вы просто пытаетесь вызвать executeCommand, и это доступно в Command ... поэтому просто напишите:

command.executeCommand();

, который должен скомпилироваться и запустить.Непонятно, откуда берется карта.

Что касается причины сбоя приведения ... моя догадка заключается в том, что ClassLoader для команды не является ClassLoader по умолчанию на этом этапеотметим, что implementorClass - это тот же класс, но загруженный другим ClassLoader ... что делает его классом различий в отношении JVM.

РЕДАКТИРОВАТЬ: я бы сказал, что ваш дизайнсломана.Проходящий мимо объект Command не выполняет свою роль должным образом.Одним из вариантов может быть новый подкласс RemoteCommand, который знает имя, и когда вызывается его метод executeCommand, он создает соответствующий экземпляр подкласса.И да, потребуется для создания экземпляра класса.Вы не можете вызывать метод экземпляра в классе без экземпляра этого класса, и вы не можете заставить один объект «притворяться», что это на самом деле объект другого типа.Что если у AuthenticationCommand есть дополнительные поля, которые он пытается использовать?Откуда берутся значения?

Более хорошая альтернатива - сделать так, чтобы ваш уровень сериализации / десериализации делал это, так что к тому времени, как вы достигли этого бита кода, вы уже получили и AuthenticationCommand - и вы можете использовать код в верхней части этого ответа.

3 голосов
/ 17 сентября 2010

Вам действительно нужно создать его экземпляр.Вы не можете "преобразовать" Class<T> в конкретный экземпляр, просто применив.Кроме того, приведение должно быть выполнено наоборот, в отличие от вашего фрагмента кода.

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

Не говоря уже о том, что все это - запах проекта.

1 голос
/ 16 сентября 2010

Вы сможете снизить рейтинг только тогда, когда объект Command действительно ссылается на экземпляр команды Authenticate во время выполнения.Это то, о чем говорит полиморфизм?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...