如何在具有不同类型值的Map中使用泛型 [英] How to use generics in a Map with values of different types

查看:1115
本文介绍了如何在具有不同类型值的Map中使用泛型的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个通用的 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命令<整数> {
公共无效执行(整数值){...}
}

我需要的是 Map 将命令名与特定的命令对象连接起来:

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

很明显,当我收到 rawtypes 警告时声明 Map
如果我使用问号,最终出现编译错误:

 映射< String,Command<> ;> commands = new HashMap<>(); 
...
commands.get(changeName)。execute(Foo); // - >编译错误




方法执行(capture#2 of?)in类型命令不适用于参数(字符串)

我知道你不能拥有一个类型安全的非可归类容器键入 中的第29项),但解决此问题的最佳方法是什么?

解决方案

我认为您需要在运行时让Commands知道他们可以接受的参数:

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

保护命令(Class< T> argumentClass){
this.argumentClass = argumentClass;
}

public abstract< U extends T>无效执行(U参数);


@SuppressWarnings(unchecked)
public final< U>命令<?超级U> cast(Class< U> argumentClass){
if(this.argumentClass.isAssignableFrom(argumentClass)){
return(Command< super U>)this;
} else {
throw new UnsupportedOperationException(这个命令不能处理类型参数+ argumentClass.getName());
}
}
}

现在使用的代码是像这样:

 私人< 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);

$ / code>

上面的压制警告是一个令人讨厌的压力警告,因为该投射必须始终为真但是作为返回 Class <?> getClass 的最终定义的限制。



地图可以输入为:

地图< String,Command< >>命令= new HashMap<>();



每个命令子类都会扩展抽象 / code> class。



例如一个匿名的内部类定义o打印字符串命令到stderr:

  final命令< 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);






$ b

如果你喜欢,你可以提取一个 Command 接口并将抽象类重命名为 AbstractCommand


I have a generic Command interface:

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) {...}
}

What I need is a Map to link command names with a specific Command object:

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

Obviously, I am getting rawtypes warnings when declaring the Map. If I use a question mark I end up with a compilation error:

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)

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

解决方案

I think you need to make the Commands aware of their acceptable argument at run-time:

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

Now the using code would be something like this:

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

The suppress-warning above is an annoying one as that cast must always be true but is a limitation of the final definition of getClass as returning Class<?>.

The map could be typed as:

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

And each command subtype class would extend of the abstract Command class.

For example an anonymous inner class definition o a print string command to stderr:

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

The standalone version:

public StdErrPrintCommand extends Command<String> {

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

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

If you prefer you could extract an Command interface and rename the abstract class as AbstractCommand.

这篇关于如何在具有不同类型值的Map中使用泛型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆