值受键限制的Java映射';s型参数

值受键限制的Java映射';s型参数,java,generics,Java,Generics,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>

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);
    }
}
公共类Foo{
//这个声明不会编译-它应该是什么?
私有静态映射默认值;
//这两种方法都很好
公共静态void setDefaultValue(类clazz,T值){
defaultValues.put(clazz,value);
}
公共静态T getDefaultValue(类clazz){
返回defaultValues.get(clazz);
}
}
也就是说,只要值的类型与类对象的类型匹配,我就可以针对类对象存储任何默认值。我不明白为什么不允许这样做,因为我可以确保在设置/获取值时类型是正确的


编辑:谢谢克莱特斯的回答。我实际上不需要映射本身的类型参数,因为我可以确保获取/设置值的方法的一致性,即使这意味着使用一些稍微难看的类型转换。

不,您不能直接这样做。您需要围绕
Map
编写一个包装类,以强制该对象将是
类的
实例。

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

}
公共类测试{
私有映射值;
public void setDefaultValue(类clazz,T值){
defaultValues.put(clazz,value);
}
公共T getDefaultValue(类clazz){
返回defaultValues.get(clazz);
}
}

或者,您可以使用Paul Tomblin的答案,并使用您自己的对象包装
映射
,该对象将强制执行此类泛型。

您不是在尝试实现Joshua Bloch的类型安全异构容器模式,是吗?基本上:

公共类收藏夹{
私有映射,对象>();
公共空间设置收藏夹(类klass,T thing){
收藏。放(克拉斯,东西);
}
公共T-getFavorite(klass类){
返回klass.cast(favorites.get(klass));
}
公共静态void main(字符串[]args){
收藏夹f=新收藏夹();
f、 setFavorite(String.class,“Java”);
f、 setFavorite(Integer.class,0xcafebabe);
String s=f.getFavorite(String.class);
inti=f.getFavorite(Integer.class);
}
}

从和。

这个问题和答案让我想到了这个解决方案:。这是代码。测试用例:

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

}
import java.util.Collection;
导入java.util.HashMap;
导入java.util.Map;
导入java.util.Set;
公共类TypedMap实现映射{
私人地图代表;
公共类型映射(映射代理){
this.delegate=委托;
}
公共类型映射(){
this.delegate=new HashMap();
}
@抑制警告(“未选中”)
public T get(TypedMapKey键){
return(T)delegate.get(key);
}
@抑制警告(“未选中”)
公用T删除(类型DMAPKEY key){
返回(T)委托。删除(键);
}
公共无效集(TypedMapKey键,T值){
delegate.put(键、值);
}
//---只给下面的代表打电话
公共空间清除(){
delegate.clear();
}
公共布尔containsKey(对象键){
返回delegate.containsKey(键);
}
公共布尔包含值(对象值){
返回委托.containsValue(值);
}
公共集入口集(){
返回delegate.entrySet();
}
公共布尔等于(对象o){
返回delegate.equals(o);
}
公共对象获取(对象密钥){
返回delegate.get(key);
}
公共int hashCode(){
返回delegate.hashCode();
}
公共布尔值为空(){
返回delegate.isEmpty();
}
公共集密钥集(){
返回delegate.keySet();
}
公共对象put(对象键、对象值){
返回delegate.put(键、值);
}

public void putAll(Map可以创建一个类,该类将类型为safe key的映射存储到一个值,并在必要时强制转换。强制转换
get
方法是安全的,就像使用
new key()之后一样
,无法将其安全地强制转换为
,因此类型系统强制执行类的正确用法

类必须是final,否则如果两个不同类型的元素相等,用户可能会重写
equals
,并导致类型不安全。或者,如果您想使用继承,尽管存在问题,也可以重写
equals
使其成为final

public final class TypeMap {
    private final Map<Key<?>, Object> m = new HashMap<>();

    public <T> T get(Key<? extends T> key) {
        // Safe, as it's not possible to safely change the Key generic type,
        // hash map cannot be accessed by an user, and this class being final
        // to prevent serialization attacks.
        @SuppressWarnings("unchecked")
        T value = (T) m.get(key);
        return value;
    }

    public <T> void put(Key<? super T> key, T value) {
        m.put(key, value);
    }

    public static final class Key<T> {
    }
}
公共最终类类型映射{

私有最终映射您可以使用以下2个类,映射类:GenericMap,映射键类:GenericKey

例如:

// Create a key includine Type definition
public static final GenericKey<HttpServletRequest> REQUEST = new GenericKey<>(HttpServletRequest.class, "HttpRequestKey");

public void example(HttpServletRequest requestToSave)
{
    GenericMap map = new GenericMap();

    // Saving value
    map.put(REQUEST, requestToSave);

    // Getting value
    HttpServletRequest request = map.get(REQUEST);
}
//创建键includine类型定义
公共静态最终GenericKey请求=新的GenericKey(HttpServletRequest.class,“HttpRequestKey”);
公共作废示例(HttpServletRequestToSave)
{
GenericMap=新建GenericMap();
//储蓄价值
map.put(请求,请求保存);
//获得价值
HttpServletRequest=map.get(请求);
}
优势

  • 它通过编译错误强制用户输入和获取正确的类型
  • 它在里面为你做外壳
  • 泛型键有助于避免每次调用put(…)get
  • 没有打字错误,比如键是“字符串”类型
通用地图
公共类GenericMap
{  
私有地图存储地图;
受保护的GenericMap()
{
storageMap=新的HashMap();
}
公共T获取(通用密钥)
{
对象值=storageMap.get(key.getKey());
如果(值==null)
{
返回null;
}
return key.getClassType().cast(v
public final class TypeMap {
    private final Map<Key<?>, Object> m = new HashMap<>();

    public <T> T get(Key<? extends T> key) {
        // Safe, as it's not possible to safely change the Key generic type,
        // hash map cannot be accessed by an user, and this class being final
        // to prevent serialization attacks.
        @SuppressWarnings("unchecked")
        T value = (T) m.get(key);
        return value;
    }

    public <T> void put(Key<? super T> key, T value) {
        m.put(key, value);
    }

    public static final class Key<T> {
    }
}
// Create a key includine Type definition
public static final GenericKey<HttpServletRequest> REQUEST = new GenericKey<>(HttpServletRequest.class, "HttpRequestKey");

public void example(HttpServletRequest requestToSave)
{
    GenericMap map = new GenericMap();

    // Saving value
    map.put(REQUEST, requestToSave);

    // Getting value
    HttpServletRequest request = map.get(REQUEST);
}
public class GenericMap
{  
    private Map<String, Object> storageMap;

    protected GenericMap()
    {
        storageMap = new HashMap<String, Object>();
    }

    public <T> T get(GenericKey<T> key)
    {
        Object value = storageMap.get(key.getKey());
        if (value == null)
        {
            return null;
        }

        return key.getClassType().cast(value);
    }

    /**
     * @param key    GenericKey object with generic type - T (it can be any type)
     * @param object value to put in the map, the type of 'object' mast be - T
     */
    public <T> void put(GenericKey<T> key, T object)
    {
        T castedObject = key.getClassType().cast(object);
        storageMap.put(key.getKey(), castedObject);
    }

    @Override
    public String toString()
    {
        return storageMap.toString();
    }
}
public class GenericKey<T>
{
    private Class<T> classType;
    private String key;

    @SuppressWarnings("unused")
    private GenericKey()
    {
    }

    public GenericKey(Class<T> iClassType, String iKey)
    {
        this.classType = iClassType;
        this.key = iKey;
    }

    public Class<T> getClassType()
    {
        return classType;
    }

    public String getKey()
    {
        return key;
    }

    @Override
    public String toString()
    {
        return "[classType=" + classType + ", key=" + key + "]";
    }
}