Java Map使用可变泛型作为值

时间:2017-04-19 23:44:35

标签: java generics type-safety

所以这对我来说是个棘手的问题。

我有一个通用对象。称之为MyObject。该对象有一个返回T:

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

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

    //...

    public T get()
    {
        return _t;
    }
}

(显然我的&#34; MyObject&#34;做得多一些,但这是要点)。

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

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

但是 - 这是棘手的部分 - 我希望地图能够记住&#34;当我从地图中获取一些值时,MyObject的参数化类型。使用

m.get("map_1");

会返回

MyObject<Object> 

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

MyObject<?> 

值。因此:

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

可以进行哪些修改(如果有的话),以便能够获得有关MyObject获取对象的正确 - 完整信息,以便调用最后一行(m.get(&#34; map_1&#34; ))会返回

MyObject<String>

谢谢:)

阿米尔。

3 个答案:

答案 0 :(得分:3)

来自Joshua Bloch的 Effective Java 的Typesafe异构容器可能适用于此处。基本上你添加一个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来投射对象。)

答案 1 :(得分:0)

你可以上课。

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

    }

}

答案 2 :(得分:0)

类型系统只知道类型而不是对象,因此无法区分"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);