我有一个通用的 Command
接口(interface):
public interface Command<T> {
public void execute(T value);
}
以及一些实现:
public class ChangeName implements Command<String>{
public void execute(String value) {...}
}
public class SetTimeout implements Command<Integer>{
public void execute(Integer value) {...}
}
我需要的是一个 Map
来将命令名称与特定的 Command
对象链接起来:
Map<String, Command> commands = new HashMap<>();
...
commands.put("changeName", new ChangeName());
显然,我在声明 Map
时收到了 rawtypes
警告。
如果我使用问号,我最终会出现编译错误:
Map<String, Command<?>> commands = new HashMap<>();
...
commands.get("changeName").execute("Foo"); // -> compilation error
The method execute(capture#2-of ?) in the type Command is not applicable for the arguments (String)
我知道您不能拥有具有不可具体化类型的类型安全异构容器(Effective Java 中的Item 29),但解决此问题的最佳方法是什么问题?
最佳答案
我认为您需要让命令在运行时知道它们可接受的参数:
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 可以输入为:
Map<String, Command<?>> commands = new HashMap<>();
并且每个命令子类型类都将扩展抽象 Command
类(class)。
例如匿名内部类定义 o 到 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
接口(interface)并将抽象类重命名为AbstractCommand
.
关于java - 如何在具有不同类型值的 Map 中使用泛型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48155477/