Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to properly mix generics and inheritance to get the desired result?

My question is not easy to explain using words, fortunately it's not too difficult to demonstrate. So, bear with me:

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

Basically, I want the generic "process" method to dictate the type of generic parameter of the Command object. The goal is to be able to restrict different implementations of CommandProcessor to certain classes that implement Command interface and at the same time to able to call the process method of any class that implements the CommandProcessor interface and have it return the object of type specified by the parametarized Command object. I'm not sure if my explanation is clear enough, so please let me know if further explanation is needed. I guess, the question is "Would this be possible to do, at all?" If the answer is "No" what would be the best work-around (I thought of a couple on my own, but I'd like some fresh ideas)

like image 956
Andrey Avatar asked Jun 12 '10 18:06

Andrey


1 Answers

Unfortunately, you cannot do this. Since you want the CommandProcessor interface to be defined in terms of Command, your implemnetation must be prepared to take any kind of Command instance - generics cannot restrict this to BasicCommand - if it could, then the BasicCommandProcessor subclass would not implement the CommandProcessor interface.

Or, from another angle, given a CommandProcessor interface, it's not possible for generics to ensure that this was only called with BasicCommand instances. To do this would need to know the implemnetation, and would go against the point of polymorphism and interfaces.

You can parameterize the result of the command, but not the concrete class of command.

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

The simplest approach is to provide a method that accepts the specific type you need, and call that in the generic method. (See BasicCommandProcessor above.)

like image 86
mdma Avatar answered Oct 05 '22 21:10

mdma