简体   繁体   中英

Weird java generic compiler behaviour on generic method

I'm trying to implement a pretty naive implementation of the command patern using generics

public abstract class Command { 
}


public interface CommandHandler<H extends Command> {

    boolean isActive();     
    void execute( H command );

}


public class CommandExecutionServiceImpl implements CommandExecutionService {

    private Map< Class<Command>,CommandHandler<Command>> commandMap; 


    public CommandExecutionServiceImpl(){       
        commandMap = new HashMap<Class<Command>, CommandHandler<Command>>();        
    }

    @Override
    public void executeCommand(Command command) {

        CommandHandler<Command> handler = commandMap.get(command.getClass());
        handler.execute(command);       

    }

    @Override
    public boolean isActive(Command command) {
        return false;
    }

    @Override
    public <H extends Command> void addCommandHandler(Class<H> commandClass, CommandHandler<H> handler) {       

        commandMap.put( commandClass, handler );

    }

Compiler fails with

Compilation failure CommandExecutionServiceImpl.java:[36,12] put(java.lang.Class,CommandHandler) in java.util.Map,CommandHandler> cannot be applied to (java.lang.Class,CommandHandler)

I can't understand why the compiler cannot infer the type at commandMap.put( commandClass, handler );

Any help would be appreciated.

It looks like you want a generic relationship between the keys and values of the map. If this were supported it might look like this:

private <H extends Command> Map<Class<H>, CommandHandler<H>> commandMap;

But that's obviously not allowed. A workaround is to use Josh Bloch's Typesafe Heterogeneous Container pattern:

private Map<Class<? extends Command>, CommandHandler<? extends Command>> commandMap; 

@Override
public <H extends Command> void executeCommand(H command) {

    // addCommandHandler guarantees the safety of this unchecked cast
    @SuppressWarnings("unchecked")
    CommandHandler<H> handler = (CommandHandler<H>)commandMap.get(command.getClass());
    handler.execute(command);
}

Your map is declared:

Map< Class<Command>,CommandHandler<Command>>

but you try to put values of type:

Class<H> commandClass, CommandHandler<H> handler

where <H extends Command>

Perhaps you should add a generic type to the CommandExecutionService of type <T extends Command> and declare the map as:

Map< Class<T>, CommandHandler<T>>

and the method:

public void addCommandHandler (Class<T> commandClass, CommandHandler<T> handler) { ... }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM