The question and the answers made me come up with this solution: Type-safe object map. Here is the code. Test case:
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 class:
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();
    }
}