2012-04-27 17 views
15

Estoy escribiendo un simple HashMap caché basada en que funciona de la siguiente manera:Java - Ampliación HashMap - Objeto vs comportamiento genéricos

  1. key Si el pedido es en la memoria caché, presentara su value.
  2. Si el pedido no es key allí, ejecutar un método que produce value basado en key, almacenar tanto, volver value.

El código:

import java.util.HashMap; 

abstract class Cache<K, V> extends HashMap<K, V> { 
    @Override 
    public V get(Object key) { 
     if (containsKey(key)) { 
      return super.get(key); 
     } else { 
      V val = getData(key); 
      put((K)key, val); // this is the line I'm discussing below 
      return val; 
     } 
    } 

    public abstract V getData(Object key); 
} 

Es bastante sencillo y funciona bien. Sin embargo, I odio la decisión del Sol para get() para tomar como argumento Object y no K. He leído lo suficiente al respecto como para saber que tiene algún fundamento (con el que no estoy de acuerdo, pero esa es otra historia).

Mi problema está en la línea comentada, porque parece que el molde tiene para ser desmarcado. Debido al borrado de tipo, no hay manera de que pueda verificar si key es del tipo K (que es necesario para la funcionalidad adecuada de put()) y el método es, por lo tanto, propenso a errores.

Una solución sería cambiar de "es una" a "tiene un" HashMap relación que es mucho más agradable y limpio, pero luego no se puede poner en práctica CacheMap que sería bueno por varias razones. El código:

import java.util.HashMap; 
import java.util.Map; 

abstract class Cache<K, V> { 
    private final Map<K, V> map = new HashMap<K, V>(); 

    public V get(K key) { 
     if (map.containsKey(key)) { 
      return map.get(key); 
     } else { 
      V val = getData(key); 
      map.put(key, val); 
      return val; 
     } 
    } 

    public abstract V getData(K key); 
} 

¿Alguien puede llegar a cualquier otra solución (incluso hacker), de modo que pudiera mantener Cache a ser un Map y todavía ser seguro en los términos de get(Object key) y put(K key, V val) tipo?

Lo único que se me ocurre es hacer otro método llamado, por ejemplo, getValue(Key k) que delegaría en get(Object key), pero no puedo obligar a nadie a usar el nuevo método en lugar del habitual.

+1

Puede implementar 'getA' o' myget' No puede cambiar el comportamiento de Map.get() ya que no puede forzar código que utiliza la interfaz en lugar de su clase directamente para ser recompilado. –

Respuesta

15

No. Has encontrado la solución adecuada para cambiar a una relación de "tiene una". (Francamente, es sorprendente que el método get calcule un nuevo valor si uno no existe ya, viola el contrato Map y puede llevar a un comportamiento extremadamente extraño para varios otros métodos. Esta fue una gran parte de por qué la guayaba se mudó desde MapMaker, que ofrecía casi este comportamiento exacto - porque era tan riddled con problemas.)

Dicho esto, qué p.ej. Cache de Guava no es expone una Map<K, V> asMap()vista, que es una cosa que usted podría hacer. Eso le brinda la mayoría de las ventajas de Map sin comprometer la seguridad del tipo.

+0

Parece que el 'MapMaker' de hecho hizo lo que hago :). Soy parcialmente consciente del comportamiento extraño. El método 'V getData (clave K)' es 'abstracto' y, por lo tanto, está destinado a ser especificado (implementado) en la creación de instancias de caché. Incluso intenté hacer la clase 'final abstracta' (aunque sabía al 100% que no funcionaría) o simplemente' final' (con el método imposible de superar por la clase interna anónima, por supuesto, tampoco funciona) para asegurar que nadie intente subclasificar esta peligrosa pieza de código con alguna lógica comercial. De todos modos, muchas gracias, la relación "tiene-a" con 'asMap()' dando una _vista_ es. –

+0

Por cierto, no puedo creer que me haya perdido el caché de guayaba. No lo usaré ahora (cuando tenga mi propia implementación) por orgullo, pero si lo supiera ayer ... :) –

+5

Usted ... debería considerar usar 'Caché' de Guava de todos modos. Se usa en la producción de Google, por lo que se ha probado y optimizado en gran medida, y tiene [lotes] (http://code.google.com/p/guava-libraries/wiki/CachesExplained) características útiles. –

0

definitivamente la relación has-a es una implementación correcta. la lógica comercial de cómo se genera el valor debe eliminarse de la clase de caché.

+0

Esa es la parte divertida: no tiene lógica. El método es 'abstracto' y, por lo tanto, debe especificarse (implementarse, anularse, lo que más le guste) en la creación de instancias de caché. Incluso intenté hacer la clase 'final abstracta' (aunque sabía al 100% que no funcionaría) o simplemente' final' (con el método imposible de superar por la clase interna anónima, por supuesto, tampoco funciona) para asegurar que nadie intente subclasificar esta peligrosa pieza de código con alguna lógica comercial. –