Подтвердить что ты не робот

Почему Java Map <K, V> принимает нетипизированный параметр для методов get и remove?

Я столкнулся с ошибкой в ​​своем коде, где я использовал неправильный ключ, чтобы извлечь что-то из карты Java, которая, как я полагала, была строго типизирована с использованием Java-дженериков. Когда вы смотрите на карту Javadocs, многие из методов, включая get и remove, принимают объект как параметр вместо типа K (для Карты, определенной как Карта). Почему это? Есть ли веская причина или это ошибка дизайна API?

4b9b3361

Ответ 1

Я думаю, что это для обратной совместимости со старыми версиями интерфейса Map. Несчастливо, что это так, но, как вы правы, было бы намного лучше, если бы это заняло правильный тип.

Ответ 2

Поскольку карта вернет значение, если объект, переданный методу get, равен любому ключу, сохраненному на карте. Equal не означает, что они должны быть одного типа, но что ключ и переданный объект равны методам, реализуются таким образом, что разные типы объектов взаимно признаются равными.

То же самое относится и к методу удаления.


Пример допустимого кода, который разбивает (не компилирует), если метод get допускает только параметры типа K:

LinkedList<Number> k1 = new LinkedList<Number>();
k1.add(10);

ArrayList<Integer> k2 = new ArrayList<Integer>();
k2.add(10);

Map<LinkedList<Number>, String> map = new HashMap<LinkedList<Number>, String>();

map.put(k1, "foo");
System.out.println(map.get(k2));

Ответ 3

Это было сделано так, что если параметр типа является подстановочным знаком, эти методы все равно можно вызвать.

Если у вас есть Map<?, ?>, Java не позволит вам вызывать любые методы, объявленные с помощью общих типов в качестве аргументов. Это не позволяет вам нарушать ограничения типа, поэтому вы не можете, например, вызвать put(key, value) с неправильными типами.

Если get() были определены как get(K key) вместо текущего get(Object key), это тоже было бы исключено из-за этого же правила. Это сделало бы подстановочную карту практически непригодной для использования.

В теории, то же самое относится к remove(), так как удаление объекта никогда не может нарушать ограничения типа.

Вот пример кода, который не был бы возможен, если get был объявлен как get(T key):

public static <K,V> Map<K, V> intersect(Map<? extends K, ? extends V> m1, Map<? extends K, ? extends V> m2) {
    Map<K,V> result = new HashMap<K, V>();
    for (Map.Entry<? extends K, ? extends V> e1 : m1.entrySet()) {
        V value = m2.get(e1.getKey()); // this would not work in case of Map.get(K key)
        if (e1.getValue().equals(value)) {
            result.put(e1.getKey(), e1.getValue());
        }
    }
    return result;
}

e1.getKey() возвращает объект некоторого неизвестного подтипа K (подтип, используемый m1), но m2 использует потенциально другой подтип K. Если Map.get() был объявлен как get(K key), это использование не было бы разрешено.