繁体   English   中英

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

[英]How to use generics in a Map with values of different types

我有一个通用的Command接口:

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

类型Command中的execute(capture#2-of?)方法不适用于参数(String)

我知道你不能拥有一个不可重类型的类型安全异构容器( Effective Java中的 第29项 ),但解决这个问题的最佳方法是什么?

如果从逻辑上考虑它,那么所有命令模板必须满足的最高通用接口是什么?

看看你的String,Integer的例子,看起来它不能只是Java Object。 试试这个,

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

编辑 :基本上,您在声明时添加模板信息,但希望在使用时完全删除它。 这里有两个选项:

a)您不使用泛型,因为您无法将它们用于潜力。 处理简单的Object类,而不是在特定的执行函数中,只测试正确的类型。

b)为不同类型创建不同的地图。 这样您就可以使用模板来发挥其潜力。

我认为你需要让命令在运行时知道它们可接受的参数:

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

上面的抑制警告是令人讨厌的,因为强制转换必须始终为true,但是将getClass的最终定义限制为返回Class<?>

地图可以输入为:

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

每个命令子类都将扩展抽象Command类。

例如,对stderr的匿名内部类定义oa print string命令:

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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