2010-02-05 22 views
14

Estoy tratando de crear un tipo genérico que guarde un mapa de las versiones de sí mismo que se han creado para su uso posterior. Efectivamente, es un patrón único donde hay una instancia por tipo. El código que tengo hasta ahora es:Mapa genérico de llaves/valores genéricos con tipos relacionados

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

Sin embargo, todavía no estoy seguro de que estoy "haciendo las cosas bien". Siento que debería ser capaz de especificar que la colección es (Clase -> FieldBinder). El hecho de que el IDE advierte sobre la declaración de devolución solo refuerza este pensamiento.

¿Hay una mejor manera de manejar esto?

Nota: This question parece muy relacionado, pero lo suficientemente lejos que no puedo encontrar la manera de aplicar la información a mi propio problema.

Respuesta

14

Su implementación es correcta. No hay manera "mejor" de hacerlo (si hay tal cosa es "mejor" en el código, que es otro tema ..)

Correcciones menores:

  • <V extends Object> es equivalente a V que es menos detallado
  • Class<? extends Object> es equivalente a Class<?> que es menos detallado
  • puede utilizar la anotación @SuppressWarnings("unchecked") de decirle a su compilador que el elenco es seguro
1

El ejemplo que refiere indica cómo recuperar el tipo (clase) de objeto, mientras que necesita recuperar el tipo (clase) de parametrización. Eso no es posible.

+0

Suponiendo que entiendo lo que dices, eso no es para nada lo que estoy haciendo. Lo que quiero es decirle al compilador "este es un mapa de 'Clase ' a 'FieldBinder ' donde ambos 'deben ser iguales. Por lo tanto, cuando saco algo usando una clave de? = Xyz, puedo hacerlo con seguridad Convery el valor para? = xyz, ya que el compilador puede limitarme a solo poner las cosas de esa manera. Es toda la información del tiempo de compilación, parece que el compilador no puede manejarlo. – RHSeeger

3

No creo que se pueda hacer sin tener un elenco sin marcar en alguna parte. Necesitaría algo similar al existential types de Haskell, que Java no tiene.

Usted podría hacer que el cliente realizar el reparto sin control en su lugar ...

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

Ahora bien, si el cliente pasa un Class<FieldBinder<V>> al método getInstance() puede evitar el reparto sin control dentro getInstance().

Desafortunadamente, la creación de un Class<FieldBinder<V>> requiere un lanzamiento sin marcar.

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

RHSeeger, recibí su pregunta original. No encontré ninguna solución para el problema. Con lo que puedes intentar jugar es una clase MyMap, que hace el enlace como lo solicitas. Sin embargo, con este mapa se plantean dos problemas:

  1. Como se ha declarado como MyMap<?>, no se puede añadir algo con un tipo dado a la misma. Eso es tonto y lo remito a Java Generics FAQs (vea el estudio de caso 3) para más detalles.
  2. Como el mapa tiene conexión entre clave y valor, no se pueden agregar dos objetos independientes de ningún tipo (dos <?> se refieren a diferentes tipos) porque estos dos tipos pueden no estar conectados.

Mientras jugaba he visto algunos errores, que no pude explicar. Creo que todo va en el hecho (como lo mencioné antes) de que tratamos de lidiar con la parametrización de nivel 2-n.

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 
     } 
    } 
+0

Gracias por la respuesta detallada. Tengo un bastante buena comprensión sobre el hecho de que los genéricos de Java simplemente no están a la altura de la tarea que estoy buscando lograr ahora, y su respuesta ayudó a solidificar eso. Creo que lo que realmente me gustaría ver sería la capacidad de decir .. .. estática final Mapa , FieldBinder > instanceMap2 = new HashMap , FieldBinder > (); .... Donde significa "cualquier tipo", pero vincula ese tipo a una variable que se puede usar más adelante. – RHSeeger

Cuestiones relacionadas