Мой вопрос не легок объяснить слова использования, к счастью, не слишком трудно продемонстрировать. Так, терпите меня:
public interface Command<R>
{
public R execute();//parameter R is the type of object that will be returned as the result of the execution of this command
}
public abstract class BasicCommand<R> implements Command<R>
{
}
public interface CommandProcessor<C extends Command<?>>
{
public <R> R process(C<R> command);//this is my question... it's illegal to do, but you understand the idea behind it, right?
}
//constrain BasicCommandProcessor to commands that subclass BasicCommand
public class BasicCommandProcessor<C extends BasicCommand<?>> implements CommandProcessor<C>
{
//here, only subclasses of BasicCommand should be allowed as arguments but these
//BasicCommand object should be parameterized by R, like so: BasicCommand<R>
//so the method signature should really be
// public <R> R process(BasicCommand<R> command)
//which would break the inheritance if the interface's method signature was instead:
// public <R> R process(Command<R> command);
//I really hope this fully illustrates my conundrum
public <R> R process(C<R> command)
{
return command.execute();
}
}
public class CommandContext
{
public static void main(String... args)
{
BasicCommandProcessor<BasicCommand<?>> bcp = new BasicCommandProcessor<BasicCommand<?>>();
String textResult = bcp.execute(new BasicCommand<String>()
{
public String execute()
{
return "result";
}
});
Long numericResult = bcp.execute(new BasicCommand<Long>()
{
public Long execute()
{
return 123L;
}
});
}
}
В основном я хочу, чтобы универсальный метод "процесса" продиктовал тип универсального параметра Объекта команды. Цель состоит в том, чтобы смочь ограничить различные реализации CommandProcessor к определенным классам, которые реализуют интерфейс Command и в то же время к способному для вызова метода процесса любого класса, который реализует интерфейс CommandProcessor, и имейте его, возвращают объект типа, указанного параметризованным Объектом команды. Я не уверен, является ли мое объяснение достаточно четким, поэтому сообщите мне, необходимо ли дальнейшее объяснение. Я предполагаю, вопрос, "Это было бы возможно сделать, вообще?" Если бы ответ является "Нет", каково было бы лучшее обходное решение (я думал о паре самостоятельно, но я хотел бы некоторые свежие идеи),
К сожалению, вы не можете этого сделать.Поскольку вы хотите, чтобы интерфейс CommandProcessor
был определен в терминах Command
, ваша реализация должна быть подготовлена к приему любого вида экземпляра Command
- универсальные шаблоны не могут ограничивать это BasicCommand
- если бы это было возможно, то подкласс BasicCommandProcessor
не реализовал бы интерфейс CommandProcessor
.
Или, с другой стороны, учитывая интерфейс CommandProcessor
, универсальные шаблоны не могут гарантировать, что он вызывается только с экземплярами BasicCommand
. Для этого необходимо знать реализацию и противоречить принципу полиморфизма и интерфейсов.
Вы можете параметризовать результат команды, но не конкретный класс команды.
public interface Command<R>
{
public R execute();//parameter R is the type of object that will be returned as the result of the execution of this command
}
public abstract class BasicCommand<R> implements Command<R>
{
}
public interface CommandProcessor
{
public <R> R process(Command<R> command);
}
public class BasicCommandProcessor implements CommandProcessor
{
public <R> R processBasicCommand(BasicCommand<R> command)
{
return command.execute();
}
public <R> R process(Command<R> command)
{
return processBasicCommand((BasicCommand<R>)command);
}
}
Самый простой подход - предоставить метод, который принимает конкретный тип, который вам нужен, и вызвать его в универсальном методе. (См. BasicCommandProcessor выше.)
В основном, я хочу, чтобы общий метод "процесс", чтобы диктовать тип общего параметра команды объекта.
Это противоречит понятию определения команды как параметра типа для охватывающего типа: При инстанцировании CommandProcessor
для C
может быть предоставлен фактический тип, такой как Command
. Можно было бы даже поставить негенерический тип, например
class Foo implements Command<String> {
...
}
Каково тогда будет значение C
? Foo
? Command
? Command
?
Итак, какие варианты у вас есть? Если CommandProcessor должен работать только с определенным типом возврата, вы можете сделать:
class CommandProcessor<R, C extends Command<R>> {
R process(C command);
}
class FancyCommandProcessor<R, C extends FancyCommand<R>> extends CommandProcessor<R,C> {
}
Однако, я подозреваю, что вы хотите, чтобы CommandProcessor работал со всем семейством типов команд. Это само по себе не проблема, просто объявите:
<R> R process(FancyCommand<R> command);
Однако, если вы дополнительно хотите иметь отношения подтипа между CommandProcessor для различных семейств команд, чтобы вы могли переопределить process
, вы выходите за пределы выразительности Java generics. В частности, вам понадобится либо эквивалент параметров типа C++ 'template' (которые позволяют передавать шаблон в качестве фактического аргумента типа), либо возможность захвата параметра типа Command, заданного фактическим аргументом типа, который, как известно, расширяет Command. Java не поддерживает ни то, ни другое.