开发者

Generic Map of Generic key/values with related types

I'm trying to create a generic type that keeps a map of the versions of itself that have been created for later use. Effectively, it's an singleton pattern where there's one instance per type. The code I have so far is:

public class FieldBinder<T> {
    static final Map<Class<? extends Object>,FieldBinder<? extends Object>> instanceMap = 
        new HashMap<Class<? extends Object>,FieldBi开发者_Go百科nder<? extends Object>>();

    private FieldBinder() {}

    synchronized public static <V extends Object> FieldBinder<V> getInstance(Class<V> klass) {
        if(!instanceMap.containsKey(klass)) {
            instanceMap.put(klass, new FieldBinder<V>());
        }
        return (FieldBinder<V>)instanceMap.get(klass);
    }
}

However, I'm still unsure that I'm "doing it right". It feels like I should be able to specify that the collection is (Class -> FieldBinder). The fact that the IDE is warning about the return statement only reinforces this thought.

Is there a better way to handle this?

Note: This question seems very closely related, but just far enough away that I can't figure out how to apply the information in it to my own problem.


Your implementation is correct. There's no "better" way of doing it (if there is such a thing is "better" in code, which is another issue..)

Minor fixes:

  • <V extends Object> is equivalent to V which is less verbose
  • Class<? extends Object> is equivalent to Class<?> which is less verbose
  • You can use the @SuppressWarnings("unchecked") annotation to tell your compiler that the cast is safe


I don't think it can be done without having an unchecked cast somewhere. You would need something similar to Haskell's existential types, which Java does not have.

You could make the client perform the unchecked cast instead...

synchronized public static <V> FieldBinder<V>
getInstance(Class<V> klass, Class<FieldBinder<V>> binderKlass) {
    if(!instanceMap.containsKey(klass)) {
        instanceMap.put(klass, new FieldBinder<V>());
    }
    return binderKlass.cast(instanceMap.get(klass));
}

Now if the client passes a Class<FieldBinder<V>> to the getInstance() method you can avoid the unchecked cast within getInstance().

Unfortunately creating a Class<FieldBinder<V>> itself requires an unchecked cast.

Class<FieldBinder<Integer>> binderKlass =
    (Class<FieldBinder<Integer>>) (Class<?>) FieldBinder.class;
BinderAssociator.getInstance(Integer.class, binderKlass);


RHSeeger, I got your original question. I found no solution for the problem. What you can try to play with is a MyMap class, which makes the binding as you request. However with this map two problems arise:

  1. As it is declared as MyMap<?>, one cannot add something with a given type to it. That's dummy and I refer you to Java Generics FAQs (see case study 3) for more details.
  2. As map has connection between key and value, one cannot add two independent objects of any type (two <?> refer to different types) because these two types may be not connected.

While playing I have seen some errors, which I could not explain myself. I think, everything goes into the fact (as I mentioned before) that we try to deal with 2-nd level parametrization.

    class FieldBinder<T> {
        static class MyMap<M> extends HashMap<Class<M>, FieldBinder<M>> {
        }
        static final MyMap<?> instanceMap1 = new MyMap<Object>();
        static final Map<Class<?>, FieldBinder<?>> instanceMap2 = new HashMap<Class<?>, FieldBinder<?>>();
        public static <V> void test() {
            Class<V> c1 = null;
            FieldBinder<V> f1 = null;
            Class<?> c2 = null;
            FieldBinder<?> f2 = null;
            instanceMap1.put(c1, f1); // error (see 1)
            instanceMap1.put(c2, f2); // error (see 2)
            instanceMap2.put(c1, f1); // ok
            instanceMap2.put(c2, f2); // ok
            instanceMap2.put(c1, f2); // wish to be an error, but ok
            instanceMap2.put(c2, f1); // wish to be an error, but ok
        }
    }


The example you refer tells, how to recover the type (class) of object, while you need to recover the type (class) of parametrization. That is not possible.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜