java - 如何在具有不同类型值的 Map 中使用泛型

标签 java generics

我有一个通用的 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/

相关文章:

java - UML:组件如何实现某些接口(interface)?

java - Camel 和 Google App Engine 没有 Spring?

java - 泛型和问号

generics - 如何为嵌套在泛型结构中的类实现运算符?

java - 我可以为泛型函数提供正确的类型信息吗?

java - 在 Java 中创建通用 ArrayList 的 ArrayList

java - 如何使用Java获取同一个键的多个值

java - 如何递归复制整个目录,包括Java中的父文件夹

java - 当 Spring 尝试加载 XML 文件时,防止 W3.org 出现 503 错误

c# - 获取应用于任何类的通用类型列表