views:

248

answers:

4

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>,FieldBinder<? 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.

+5  A: 

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
Itay
A: 

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.

dma_k
Assuming I understand what you're saying, then that's not at all what I'm doing. What I want is to tell the compiler "this is a map from 'Class<?>' to 'FieldBinder<?>' where both ? must be the same. As such, when I pull out something using a key of ?=xyz, I can safely convery the value to ?=xyz, since the compiler can limit me to only putting things in that way. It's all compile time information, it just seems that the compiler can't handle it.
RHSeeger
+2  A: 

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);
finnw
+1  A: 

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
        }
    }
dma_k
Thanks for the detailed answer. I have a pretty good grasp on the fact that Java generics are just not up to the task I'm looking to accomplish now, and your answer helped solidify that. I think what I'd really like to see would be the ability to say.... static final <?1> Map<Class<?1>, FieldBinder<?1>> instanceMap2 = new <?1> HashMap<Class<?1>, FieldBinder<?1>>();....Where the <?1> means "any type", but binds that type to a variable that can be used later.
RHSeeger