简体   繁体   English

以可变泛型作为值的Java Map

[英]Java Map with variable generics as values

So here's a slightly tricky question (for me). 所以这是一个对我来说有点棘手的问题。

I have a generic object. 我有一个通用对象。 Call it MyObject. 称之为MyObject。 This object has a method which returns something of the type T: 这个对象有一个返回类型T的方法:

public class MyObject<T>
{
    private T _t;

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

    //...

    public T get()
    {
        return _t;
    }
}

(Obviously my "MyObject" does a bit more but that's the gist). (显然,我的“ MyObject”的作用要大一些,但这是要点)。

Now, I want to have a map of this type: 现在,我想要一张这种类型的地图:

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

I want to be able to fetch maps using some predefined string name, and these maps can be of any MyObject. 我希望能够使用一些预定义的字符串名称来获取映射,并且这些映射可以是任何MyObject的。 For example, I could call: 例如,我可以打电话给:

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

etc. 等等

But - and here's the tricky part - I want the map to "remember" the parameterized type of MyObject when I fetch some value from the map. 但是-这是棘手的部分-当我从地图中获取某些值时,我希望地图“记住” MyObject的参数化类型。 Using 使用

m.get("map_1");

would return a 会返回一个

MyObject<Object> 

type, since the map was defined as containing 类型,因为地图被定义为包含

MyObject<?> 

values. 价值观。 Thus: 从而:

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

What modification (if any) is possible, in order to be able to get the correct - full - information regarding the MyObject fetched object, such that invoking the last line (m.get("map_1")) would return a 为了能够获取有关MyObject获取的对象的正确的完整信息,可以进行任何修改(如果有的话),以便调用最后一行(m.get(“ map_1”))将返回a

MyObject<String>

Thanks :) 谢谢 :)

Amir. 阿米尔。

Typesafe Heterogeneous Containers from Joshua Bloch's Effective Java might work here. Joshua Bloch的Effective Java中的 Typesafe异构容器可能在这里起作用。 Basically you add a Class object to represent the type. 基本上,您添加了一个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; }
}

Then you could do something like this: 然后,您可以执行以下操作:

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

Which is safe and will compile, but will throw a runtime error if you get the type wrong. 这是安全的,可以编译,但是如果您输入的类型错误,则会抛出运行时错误。

(Note I didn't actually compile that, so I might have syntax errors floating around. But most folks don't know how to use Class to cast objects.) (请注意,我实际上并没有进行编译,因此可能会出现语法错误。但是大多数人都不知道如何使用Class来转换对象。)

You can get the class. 你可以上课。

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

Here is a full test. 这是一个完整的测试。

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

    }

}

The type system only knows about types, not objects, and therefore can not distinguish "key1" from "key2" , because both are of type String . 类型系统只知道类型,而不知道对象,因此不能区分"key1""key2" ,因为两者都是String类型。

If keys have different types, the easiest way is to encapsulate a weakly typed map, and use reflective casts to prove to the compiler the types are correct: 如果键具有不同的类型,则最简单的方法是封装弱类型映射,并使用反射型强制转换向编译器证明类型正确:

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

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

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