Как я могу связать значения параметров универсального типа в интерфейсе Java-карты? - PullRequest
4 голосов
/ 10 ноября 2011

Не думаю, что название вопроса будет понятным, но идея проста.

Предположим, у меня есть переменная типа Map.

Map<K,V> myMap;

, но я хочу, например, установить связь между K и V. Я хотел бы сказать, что эта карта связана с наборамикакой-то класс для объектов этого класса.Что-то вроде:

Map<Set<T>, T> myMap;

, но не для определенного типа T. Я хотел бы, чтобы эта карта принимала записи типа

(Set<String>, String),
(Set<Integer>, Integer)
...

Есть ли возможное объявление для myMap, которое позволяет мнеесть такое поведение?Пожалуйста, дайте мне знать, если я объясняю себя неправильно или у меня есть предыдущая концептуальная ошибка.

Ответы [ 4 ]

2 голосов
/ 11 ноября 2011

К сожалению, это не возможно с обобщениями Java. Если бы Java допускала параметры типа более высокого порядка , то можно было бы определить Map что-то вроде:

public interface Map<V<>> {  // here V<> is my hypothetical syntax for a 
                             // type parameter which is itself generic...
    <K>
    V<K> put(K key, V<K> value);
    ...
}

вместо фактического java.util.Map:

public interface Map<K, V> {
    V put(K key, V value);
    ...
}

Вы видите, что проблема в том, что K объявляется один раз для всего класса, а не для каждого вызова .put().

Хватит фантазировать, так что вы можете сделать? Я думаю, что лучше всего создать Map<Set<?>, Object> и обернуть его в качестве частного участника. Тогда вы можете создавать свои собственные put() и get(), которые принимают во внимание предполагаемое «отношение» между типами:

class SpecialMap {
    private Map<Set<?>, Object> map = ...;

    public <T>
    T put(Set<T> key, T value) {
        return (T) map.put(key, value);
    }

    public <T>
    T get(Set<T> key) {
        return (T) map.get(key);
    }
}
1 голос
/ 11 ноября 2011

То, что вы пытаетесь сделать, не похоже на хорошую смерть, потому что каждый Set<T> всегда не равен другому Set<T>, даже если он того же типа - использование Sets в качестве ключей более или менее бесполезно.

Тем не менее, вам не нужно определять новый класс - вы можете требовать метод для принятия такой карты:

public static <T> void process(Map<Set<T>, T> map) {
    for (Map.Entry<Set<T>, T> entry : map) {
        Set<T> key = entry.getKey();
        T value = entry.getValue();
        // do something
    }
}
0 голосов
/ 11 ноября 2011

Я не думаю, что можно добиться проверки во время компиляции с помощью обобщений Java. Однако это довольно просто во время выполнения. Просто короткий декоратор:

public class FancyTypeMapDecorator implements Map<Set<? extends Object>, Object> {

    final Map<Set<? extends Object>, Object> target;

    public FancyTypeMapDecorator(Map<Set<? extends Object>, Object> target) {
        this.target = target;
    }

    @Override
    public Object put(Set<? extends Object> key, Object value) {
        final Class<?> keyElementType = key.iterator().next().getClass();
        final Class<?> valueType = value.getClass();
        if (keyElementType != valueType) {
            throw new IllegalArgumentException(
                "Key element type " + keyElementType + " does not match " + valueType);
        }
        return target.put(key, value);
    }

    @Override
    public void putAll(Map<? extends Set<? extends Object>, ? extends Object> m) {
        for (Entry<? extends Set<? extends Object>, ? extends Object> entry : m.entrySet()) {
            put(entry.getKey(), entry.getValue());
        }
    }

    //remaining methods are simply delegating to target

}

Вот как это работает:

final Map<Set<? extends Object>, Object> map =
    new FancyTypeMapDecorator(new HashMap<Set<? extends Object>, Object>());

Set<? extends Object> keyA = Collections.singleton(7);
map.put(keyA, 42);

Set<? extends Object> keyB = Collections.singleton("bogus");
map.put(keyB, 43);

Второй put выдает исключение.

Однако и реализация (и я даже не имею в виду, что она потерпит неудачу для пустого Set в качестве ключа) и использование / API вызывают сигнал тревоги ... Вы действительно хотите иметь дело с такой структурой? Может быть, вам нужно переосмыслить свою проблему? Чего вы на самом деле пытаетесь достичь?

0 голосов
/ 11 ноября 2011

При использовании обобщений невозможно, чтобы компилятор проверял разные T для каждого put() вызова.Другими словами, вы не можете иметь такую ​​же карту и делать:

myMap.put(new HashSet<String>(), "foo");
myMap.put(new HashSet<Integer>(), 1);

Если вам это нужно, возможно, вам придется хранить <Object> ивыполните проверку самостоятельно, используя instanceof или другой способ взлома.

Теперь вы определенно можете сделать что-то вроде:

public class MyMap<T> extends HashMap<Set<T>, T> {
    ...

Тогда вы можете сделать:

MyMap<String> myMap = new MyMap<String>();
Set<String> set = new HashSet<String>();
myMap.put(set, "foo");

Помните, что ключ должен иметь действительные hashCode() и equals() методы, которые могут быть дорогими с Set.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...