Как правильно смешать дженерики и наследование для получения желаемого результата?

Мой вопрос не легок объяснить слова использования, к счастью, не слишком трудно продемонстрировать. Так, терпите меня:

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, и имейте его, возвращают объект типа, указанного параметризованным Объектом команды. Я не уверен, является ли мое объяснение достаточно четким, поэтому сообщите мне, необходимо ли дальнейшее объяснение. Я предполагаю, вопрос, "Это было бы возможно сделать, вообще?" Если бы ответ является "Нет", каково было бы лучшее обходное решение (я думал о паре самостоятельно, но я хотел бы некоторые свежие идеи),

6
задан Andrey 12 June 2010 в 19:55
поделиться

2 ответа

К сожалению, вы не можете этого сделать.Поскольку вы хотите, чтобы интерфейс 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 выше.)

3
ответ дан 17 December 2019 в 07:00
поделиться

В основном, я хочу, чтобы общий метод "процесс", чтобы диктовать тип общего параметра команды объекта.

Это противоречит понятию определения команды как параметра типа для охватывающего типа: При инстанцировании 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 не поддерживает ни то, ни другое.

1
ответ дан 17 December 2019 в 07:00
поделиться
Другие вопросы по тегам:

Похожие вопросы: