How to use generics on a map with values ​​of different types

I have a common interface Command:

public interface Command<T> {
    public void execute(T value);
}

And some implementations:

public class ChangeName implements Command<String>{
    public void execute(String value) {...}
}
public class SetTimeout implements Command<Integer>{
    public void execute(Integer value) {...}
}

I need to Mapbind command names to a specific object Command:

Map<String, Command> commands = new HashMap<>();
...
commands.put("changeName", new ChangeName());

Obviously, I get warnings rawtypeswhen declaring Map. If I use a question mark, I get a compilation error:

Map<String, Command<?>> commands = new HashMap<>();
...
commands.get("changeName").execute("Foo"); // -> compilation error

Executing a method (capture # 2-of?) In a type command is not applicable for arguments (String)

I know that you cannot have a varied heterogeneous container with a non-recoverable type ( Item 29 in Effective Java), but what is the best approach to solve this problem?

+4
source share
2 answers

, :

public abstract class Command<T> {
    private final Class<T> argumentClass;

    protected Command(Class<T> argumentClass) {
        this.argumentClass = argumentClass;
    }

    public abstract <U extends T> void execute(U argument);


    @SuppressWarnings("unchecked")
    public final <U> Command<? super U> cast(Class<U> argumentClass) {
        if (this.argumentClass.isAssignableFrom(argumentClass)) {
           return (Command<? super U>) this;
        } else {
           throw new UnsupportedOperationException("this command cannot handle argument of type " + argumentClass.getName());
        }
    }
}

:

private <U> void executeCommand(final String name, final U arg) {
     @SuppressWarnings("unchecked")
     Class<U> clazz = (Class<U>) arg.getClass();
     commands.get(name).cast(clazz).execute(arg);
}

- , , getClass Class<?>.

:

Map<String, Command<?>> commands = new HashMap<>();

Command.

, o print string stderr:

final Command<String> printString = new Command<String>(String.class) {
    public <U extends String> void execute(U arg) {
        System.err.println(arg);
    }
};

:

public StdErrPrintCommand extends Command<String> {

     public StdErrPrintCommand() { super(String.class); }

     @Override
     public <U extends String> void excecute(U arg) { 
            System.err.println(arg);
     }
} 

, Command AbstractCommand.

+1

, , ?

String, Integer, , , Java Object. ,

Map<String, Command<? extends Object>> commands = new HashMap<>();

Edit: , , . :

a) , . Object .

b) . , .

+1

Source: https://habr.com/ru/post/1691885/


All Articles