2009-01-06 72 views
28

在Java中有一种方法可以将值的类型参数绑定到键的类型参数上吗?我想写的东西像下面这样:具有值由键的类型参数限制的Java地图

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

也就是说,我可以存储针对Class对象的任何默认值,所提供的值的类型匹配的对象的类。我不明白为什么这不应该被允许,因为我可以确保设置/获取类型是否正确的值。

编辑:谢谢cletus他的回答。我实际上并不需要地图本身的类型参数,因为我可以确保get/set方法的一致性,即使这意味着使用一些稍微丑陋的转换。

回答

47

你不是想要实现Joshua Bloch的类型安全的异构容器模式吗?基本上是:

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

Effective Java (2nd edition)this presentation

+27

如果该值本身是通用的,该怎么办?例如,不是存储`String`和`int`,你需要存储`PrettyPrinter `,其中`T`是用作映射中键的类型标记? – Lucas 2012-07-14 22:33:57

2

不,你不能直接做。您需要编写一个围绕Map<Class, Object>的包装类来强制执行该对象将是instanceof类。

-2

T作为类型必须在类实例中一般定义。下面的示例工作:

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

} 

或者,你可以使用保罗汤布林的回答,并包裹Map用自己的对象,将执行这种类型的仿制药。

+6

的海报希望有从任意类映射到默认这些类的值。这允许仅来自一个类的映射。 – Avi 2009-01-06 14:02:47

2

这个问题和答案让我想出了这个解决方案:Type-safe object map。这是代码。测试用例:

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

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

}