2009-01-06 6 views
28

Existe-t-il un moyen en Java d'avoir une carte où le paramètre type d'une valeur est lié au paramètre type d'une clé? Ce que je veux écrire quelque chose comme ce qui suit:Carte Java avec des valeurs limitées par le paramètre de type de clé

public class Foo { 
    // This declaration won't compile - what should it be? 
    private static Map<Class<T>, T> defaultValues; 

    // These two methods are just fine 
    public static <T> void setDefaultValue(Class<T> clazz, T value) { 
     defaultValues.put(clazz, value); 
    } 

    public static <T> T getDefaultValue(Class<T> clazz) { 
     return defaultValues.get(clazz); 
    } 
} 

qui est, je peux stocker une valeur par défaut contre un objet de classe, à condition type de la valeur correspond à celle de l'objet de classe. Je ne vois pas pourquoi cela ne devrait pas être autorisé puisque je peux m'assurer lors de la configuration/obtention de valeurs que les types sont corrects.

EDIT: Merci à Cletus pour sa réponse. Je n'ai pas vraiment besoin des paramètres de type sur la carte elle-même, car je peux assurer la cohérence dans les méthodes qui obtiennent/définissent des valeurs, même si cela implique d'utiliser des distributions légèrement laides.

Répondre

47

Vous n'essayez pas d'implémenter le modèle de conteneur hetereogeneous de typesafe de Joshua Bloch êtes-vous? En gros:

public class Favorites { 
    private Map<Class<?>, Object> favorites = 
    new HashMap<Class<?>, Object>(); 

    public <T> void setFavorite(Class<T> klass, T thing) { 
    favorites.put(klass, thing); 
    } 

    public <T> T getFavorite(Class<T> klass) { 
    return klass.cast(favorites.get(klass)); 
    } 

    public static void main(String[] args) { 
    Favorites f = new Favorites(); 
    f.setFavorite(String.class, "Java"); 
    f.setFavorite(Integer.class, 0xcafebabe); 
    String s = f.getFavorite(String.class); 
    int i = f.getFavorite(Integer.class); 
    } 
} 

De Effective Java (2nd edition) et this presentation.

+27

Et si la valeur elle-même était générique? Par exemple, au lieu de stocker 'String's et' int's vous devez stocker 'PrettyPrinter ' où 'T' est le jeton de type utilisé comme clé dans la carte? – Lucas

+3

@Lucas Guava fournit ['TypeToInstanceMap'] (https://google.github.io/guava/releases/snapshot/api/docs/com/google/common/reflect/TypeToInstanceMap.html) à cette fin; avec ['ClassToInstanceMap'] (https://github.com/google/guava/wiki/NewCollectionTypesExplained#classtoinstancemap) qui réplique l'API' Favorites' de Bloch. – dimo414

2

Non, vous ne pouvez pas le faire directement. Vous devrez écrire une classe wrapper autour de Map<Class, Object> pour appliquer cet objet à la classe instanceof.

-2

T comme type doit être défini de manière générique dans l'instance de classe. L'exemple suivant fonctionne:

public class Test<T> { 

    private Map<Class<T>, T> defaultValues; 

    public void setDefaultValue(Class<T> clazz, T value) { 
     defaultValues.put(clazz, value); 
    } 

    public T getDefaultValue(Class<T> clazz) { 
     return defaultValues.get(clazz); 
    } 

} 

Vous pouvez également utiliser la réponse de Paul Tomblin, et envelopper le Map avec votre propre objet qui mettra en application ce type de médicaments génériques.

+6

L'affiche veut avoir une cartographie des classes arbitraires aux valeurs par défaut de ces classes. Cela permet un mappage à partir d'une seule classe. – Avi

2

La question et les réponses m'ont amené à trouver cette solution: Type-safe object map. Voici le code. Cas de test:

import static org.junit.Assert.*; 

import java.util.ArrayList; 
import java.util.List; 

import org.junit.Test; 


public class TypedMapTest { 
    private final static TypedMapKey<String> KEY1 = new TypedMapKey<String>("key1"); 
    private final static TypedMapKey<List<String>> KEY2 = new TypedMapKey<List<String>>("key2"); 

    @Test 
    public void testGet() throws Exception { 

     TypedMap map = new TypedMap(); 
     map.set(KEY1, null); 
     assertNull(map.get(KEY1)); 

     String expected = "Hallo"; 
     map.set(KEY1, expected); 
     String value = map.get(KEY1); 
     assertEquals(expected, value); 

     map.set(KEY2, null); 
     assertNull(map.get(KEY2)); 

     List<String> list = new ArrayList<String>(); 
     map.set(KEY2, list); 
     List<String> valueList = map.get(KEY2); 
     assertEquals(list, valueList); 
    } 
} 

classe Key:

public class TypedMapKey<T> { 
    private String key; 

    public TypedMapKey(String key) { 
     this.key = key; 
    } 

    @Override 
    public int hashCode() { 
     final int prime = 31; 
     int result = 1; 
     result = prime * result + ((key == null) ? 0 : key.hashCode()); 
     return result; 
    } 

    @Override 
    public boolean equals(Object obj) { 
     if(this == obj) { 
      return true; 
     } 
     if(obj == null) { 
      return false; 
     } 
     if(getClass() != obj.getClass()) { 
      return false; 
     } 
     TypedMapKey<?> other = (TypedMapKey<?>) obj; 
     if(key == null) { 
      if(other.key != null) { 
       return false; 
      } 
     } else if(!key.equals(other.key)) { 
      return false; 
     } 
     return true; 
    } 

    @Override 
    public String toString() { 
     return key; 
    } 
} 

TypedMap.java:

import java.util.Collection; 
import java.util.HashMap; 
import java.util.Map; 
import java.util.Set; 

public class TypedMap implements Map<Object, Object> { 
    private Map<Object, Object> delegate; 

    public TypedMap(Map<Object, Object> delegate) { 
     this.delegate = delegate; 
    } 

    public TypedMap() { 
     this.delegate = new HashMap<Object, Object>(); 
    } 

    @SuppressWarnings("unchecked") 
    public <T> T get(TypedMapKey<T> key) { 
     return (T) delegate.get(key); 
    } 

    @SuppressWarnings("unchecked") 
    public <T> T remove(TypedMapKey<T> key) { 
     return (T) delegate.remove(key); 
    } 

    public <T> void set(TypedMapKey<T> key, T value) { 
     delegate.put(key, value); 
    } 

    // --- Only calls to delegates below 

    public void clear() { 
     delegate.clear(); 
    } 

    public boolean containsKey(Object key) { 
     return delegate.containsKey(key); 
    } 

    public boolean containsValue(Object value) { 
     return delegate.containsValue(value); 
    } 

    public Set<java.util.Map.Entry<Object, Object>> entrySet() { 
     return delegate.entrySet(); 
    } 

    public boolean equals(Object o) { 
     return delegate.equals(o); 
    } 

    public Object get(Object key) { 
     return delegate.get(key); 
    } 

    public int hashCode() { 
     return delegate.hashCode(); 
    } 

    public boolean isEmpty() { 
     return delegate.isEmpty(); 
    } 

    public Set<Object> keySet() { 
     return delegate.keySet(); 
    } 

    public Object put(Object key, Object value) { 
     return delegate.put(key, value); 
    } 

    public void putAll(Map<? extends Object, ? extends Object> m) { 
     delegate.putAll(m); 
    } 

    public Object remove(Object key) { 
     return delegate.remove(key); 
    } 

    public int size() { 
     return delegate.size(); 
    } 

    public Collection<Object> values() { 
     return delegate.values(); 
    } 

} 
Questions connexes