如何正确混合泛型和继承以获得所需的结果?

时间:2010-06-12 18:56:33

标签: java generics inheritance

我的问题并不容易用文字来解释,幸运的是,它并不太难以证明。所以,请耐心等待:

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

基本上,我希望通用的“进程”方法来指定Command对象的泛型参数的类型。目标是能够将CommandProcessor的不同实现限制为实现Command接口的某些类,同时能够调用实现CommandProcessor接口的任何类的进程方法,并让它返回由parametarized命令对象。我不确定我的解释是否足够清楚,如果需要进一步说明,请告诉我。我想,问题是“这根本可以吗?”如果答案是“否”,最好的解决办法是什么(我自己想到了一对夫妇,但我想要一些新鲜的想法)

2 个答案:

答案 0 :(得分:3)

不幸的是,你不能这样做。由于您希望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。)

答案 1 :(得分:1)

  

基本上,我想要通用   决定类型的“过程”方法   命令的泛型参数   对象

这与将命令定义为封闭类型的类型参数的概念不一致:在实例化CommandProcessor时,可以为Command<String>提供实际类型,例如C 。甚至可以提供非泛型类型,例如

class Foo implements Command<String> {
    ...
}

那么C<R>的含义是什么? Foo<R>Command<String><R>Command<R>

那你有什么选择?如果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);

但是,如果您还希望CommandProcessors之间的子类型关系适用于不同的命令族,那么您可以覆盖process,那么您将超越Java泛型的表现力。具体来说,您需要等效的C ++'模板'类型参数(允许将模板作为实际类型参数传递),或者需要捕获Command的类型参数的功能,给定一个已知扩展Command的实际类型参数。 Java既不支持也不支持。