Java 以变量泛型作为值的映射

Java Map with variable generics as values

所以这是一个有点棘手的问题(对我来说)。

我有一个通用对象。称它为我的对象。这个对象有一个 return 类型为 T:

的方法
public class MyObject<T>
{
    private T _t;

    public MyObject(T t)
    {
        _t = t;
    }

    //...

    public T get()
    {
        return _t;
    }
}

(显然我的 "MyObject" 做得更多,但这就是要点)。

现在,我想要一张这种类型的地图:

Map<String, MyObject<?>> m = new HashMap<>();

我希望能够使用一些预定义的字符串名称来获取地图,并且这些地图可以是任何 MyObject。例如,我可以调用:

m.put("map_1", new MyObject<String>("String"));
m.put("map_2", new MyObject<Integer>(new Integer(3));
m.put("map_3", new MyObject<Long>(new Long(5));

等等

但是 - 这是棘手的部分 - 当我从地图中获取一些值时,我希望地图 "remember" MyObject 的参数化类型。使用

m.get("map_1");

会return一个

MyObject<Object> 

类型,因为地图被定义为包含

MyObject<?> 

值。因此:

m.get("map_1").get() // <-- This is an Object, not a String! 

为了能够获得有关 MyObject 获取的对象的正确 - 完整 - 信息,可以进行哪些修改(如果有的话),以便调用最后一行 (m.get("map_1")) 会 return 一个

MyObject<String>

谢谢:)

阿米尔。

您可以获得 class.

Class c = m.get("map_1").get().getClass();
if (String.class.equals(c)) {
    System.out.println("its a String");
}

这是一个完整的测试。

public class GenericsTest {

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {

        Map<String, MyObject<?>> map = new HashMap<>();
        MyObject<String> obj = new MyObject<>("hello");

        map.put("greeting", obj);

        Class c = map.get("greeting").get().getClass();
        if (String.class.equals(c)) {
            System.out.println("its a String");
        }

    }

    static class MyObject<T> {

        T t;

        public MyObject(T t) {
            this.t = t;
        }

        T get() {
            return t;
        }

    }

}

来自 Joshua Bloch 的 Effective Java 的类型安全异构容器可能在这里工作。基本上你添加一个 Class 对象来表示类型。

public class MyObject<T>
{
    private T _t;
    private Class<T> type;

    public MyObject( Class<T> type, T t)
    {
        _t = t;
        this.type = type;
    }

    //...

    public T get()
    {
        return _t;
    }

    public Class<T> getType() { return type; }
}

那么你可以这样做:

public <T> T get( Map<String, MyObject<?>> map, String key, Class<T> type ) {
   return type.cast( m.get( key ).get() );
}

它是安全的并且可以编译,但是如果你输入错误的类型会抛出运行时错误。

(请注意,我实际上并没有编译它,所以我可能会遇到语法错误。但是大多数人不知道如何使用 Class 来转换对象。)

类型系统只知道类型,不知道对象,因此无法区分"key1""key2",因为两者都是String.

类型

如果键有不同的类型,最简单的方法是封装一个弱类型映射,并使用反射转换来向编译器证明类型是正确的:

class Favorites {

    private Map<Class<?>,?> map = new HashMap<>();

    <V> V get(Class<V> clazz) {
        return clazz.cast(map.get(clazz));
    }

    <V> void put(Class<V> clazz, V value) {
        map.put(clazz, value);
    }
}

Favorites favs = new Favorites();
favs.put(String.class, "hello");
favs.put(Integer.class, 42);
favs.get(String.class).charAt(1);