Mapa Java com valores limitados pelo parâmetro de tipo da chave

Existe uma maneira em Java para ter um mapa onde o parâmetro de tipo de um valor está vinculado ao parâmetro de tipo de uma chave? O que eu quero escrever é algo como o seguinte:

public class Foo { // This declaration won't compile - what should it be? private static Map<Class, T> defaultValues; // These two methods are just fine public static  void setDefaultValue(Class clazz, T value) { defaultValues.put(clazz, value); } public static  T getDefaultValue(Class clazz) { return defaultValues.get(clazz); } } 

Ou seja, posso armazenar qualquer valor padrão em um object Class, desde que o tipo do valor corresponda ao do object Class. Não vejo por que isso não deve ser permitido, já que posso garantir que ao definir / obter valores que os tipos estejam corretos.

EDIT: Graças a cletus por sua resposta. Eu realmente não preciso dos parâmetros de tipo no próprio mapa, já que posso garantir a consistência nos methods que obtêm / definem valores, mesmo que isso signifique usar alguns moldes ligeiramente feios.

Você não está tentando implementar o padrão de recipiente heterogêneo typesafe de Joshua Bloch, não é? Basicamente:

 public class Favorites { private Map, Object> favorites = new HashMap, Object>(); public  void setFavorite(Class klass, T thing) { favorites.put(klass, thing); } public  T getFavorite(Class 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); } } 

From Effective Java (2ª edição) e esta apresentação .

Não, você não pode fazer isso diretamente. Você precisará escrever uma class wrapper em torno de Map para impor que Object seja instanceof Class.

A pergunta e as respostas me fizeram chegar a essa solução: Mapa de objects com proteção de tipos . Aqui está o código. Caso de teste:

 import static org.junit.Assert.*; import java.util.ArrayList; import java.util.List; import org.junit.Test; public class TypedMapTest { private final static TypedMapKey KEY1 = new TypedMapKey( "key1" ); private final static TypedMapKey> KEY2 = new TypedMapKey>( "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 list = new ArrayList (); map.set( KEY2, list ); List valueList = map.get( KEY2 ); assertEquals( list, valueList ); } } 

Classe chave:

 public class TypedMapKey { 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 { private Map delegate; public TypedMap( Map delegate ) { this.delegate = delegate; } public TypedMap() { this.delegate = new HashMap(); } @SuppressWarnings( "unchecked" ) public  T get( TypedMapKey key ) { return (T) delegate.get( key ); } @SuppressWarnings( "unchecked" ) public  T remove( TypedMapKey key ) { return (T) delegate.remove( key ); } public  void set( TypedMapKey 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> 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 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 values() { return delegate.values(); } } 

T como um tipo deve ser definido genericamente na instância da class. O exemplo a seguir funciona:

 public class Test { private Map, T> defaultValues; public void setDefaultValue(Class clazz, T value) { defaultValues.put(clazz, value); } public T getDefaultValue(Class clazz) { return defaultValues.get(clazz); } } 

Alternativamente, você pode usar a resposta de Paul Tomblin e encapsular o Map com seu próprio object, o que reforçará esse tipo de genérico.