Ограничение типа объекта для метода get в Java HashMap

Я создал свой HashMap следующим образом:

Map<String, Integer> myHashMap = new HashMap<String, Integer>();

Тип данных Key - String, поэтому, когда я пытаюсь вставить новую пару ключ-значение в карту, сохраняя Key как Integer, он выдает ошибку.

myHashMap.put(1L, "value");

Это означает, что в методе put они ограничивают тип данных Key. Но при получении значения с карты с помощью метода get он не проверяет тип данных Key. Так что, если я напишу что-то вроде этого, это не даст ошибку компиляции.

myHashMap.get(1L);

Я проверил метод get в интерфейсе Java Map и его тип параметра - Object, поэтому он разрешает использование любого Object в качестве аргумента метода put.

V get(Object key)

Есть ли способ, которым я могу ограничить тип данных, который я передаю в качестве аргумента в методе get?

Аргумент, который я передаю, должен иметь тот же тип данных, что и тип данных Key который я использую при создании своей хэш-карты.

Ответ 1

Он спроектирован таким образом, поскольку во время операции get для определения возвращаемого объекта используются только equals и hashCode. Реализация метода get не проверяет тип объекта, используемого в качестве ключа.

В вашем примере вы пытаетесь получить значение, передавая long как myHashMap.get(1L); во-первых, хеш-код объекта Long со значением 1L будет использоваться для определения сегмента, из которого нужно искать. Затем метод equals ключа используется, чтобы узнать точную запись карты, из которой нужно вернуть значение. И в четко определенном методе equals всегда есть проверка для типа:

public boolean equals(Object obj) {
    if (obj instanceof Long) { //here type is checked
        return value == ((Long)obj).longValue();
    }
    return false;
}

Таким образом, если типы не равны, метод equals возвращает false и, следовательно, get также возвращает null.

В некоторых случаях, например при использовании List в качестве ключа, может случиться так, что вы поместите элемент на карту, используя экземпляр, скажем, ArrayList но вы можете успешно извлечь то же значение с помощью экземпляра LinkedList. Так как оба реализуют интерфейс List.

Map<List<String>, String> myHashMap = new HashMap<>();
List<String> arrayList = new ArrayList<>();
List<String> linkedList = new LinkedList<>();
myHashMap.put(arrayList, "foo");
System.out.println(myHashMap.get(linkedList));

Приведенный выше код будет выводиться в консоли foo.

Здесь, хотя реализации отличаются, но если вы исследуете метод equals ArrayList, он только проверяет, является ли тип List:

public boolean equals(Object o) {
        if (o == this) {
            return true;
        }

        if (!(o instanceof List)) { //checking type of super interface
            return false;
        }
        ...
}

То же самое относится и к LinkedList.

Ответ 2

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

У нас есть все возможности HashMap, и мы должны просто использовать метод getValue вместо метода get.

import java.util.HashMap;

public class MyHashMap<K,V> extends HashMap<K,V> {

    public V getValue(K key) {
        return super.get(key);
    }
}

Тестовый класс:

 public class Test {
     public static void main(String[] args) {
         MyHashMap<String,Integer> map = new MyHashMap();
     }
 }