15

I have instantiated my HashMap like this:

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

The datatype of the Key is String, so when I try to insert a new key-value pair in the map keeping the Key as Integer, it throws an error.

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

That means in the put method they have restricted the datatype of the Key. But while fetching the value from the map using the get method it is not checking for the datatype of the Key. So if I write something like this, it doesn't give a compilation error.

myHashMap.get(1L); 

I checked the get method in the Java Map interface and its parameter type is Object, so that's why it is allowing any Object as the put method argument.

V get(Object key) 

Is there a way I can restrict the datatype which I pass as an argument in the get method?

The argument that I pass should have the same datatype as the datatype of the Key which I use while instantiating my hashmap.

4
  • 1
    No. But good IDEs will warn you when doing that. And you can encapsulate the map into your own class. Commented Apr 22, 2019 at 7:16
  • In other languages you could create extension method, like getTyped.... In Java you can do this with static method, so your call will be getTyped(map, key). Ugly, but works. Commented Apr 22, 2019 at 7:22
  • I have considered the option of creating a wrapper method over the get method, something like this: Integer getMapValue(Map map, String Key){ return map.get(key) } And call this method instead of the get method, but I wanted to know if Java provides any such restricting feature of not? Commented Apr 22, 2019 at 7:30
  • You can add an implementation of a get(K key) method, but It seems like an unnecessary effort. The return value for a mismatching key (type-wise) will be null, as expected. So the Map will conform to its API. Commented Apr 22, 2019 at 7:37

2 Answers 2

20

It is designed that way, since during the get operation only the equals and hashCode is used to determine the object to be returned. The implementation of the get method does not check for the type of the Object used as the key.

In your example you are trying to get the value by passing a long like myHashMap.get(1L);, firstly the hash code of the object Long having the value 1L will be used to determine the bucket from which to look for. Next the equals method of the key is used to find out the exact entry of the map from which to return the value. And in a well-defined equals method there is always a check for the type:

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

So if the types are not equal, the equals method returns false and hence get also will return null.

In some cases such as when using List as a key, it may happen that you put an item in the map using an instance of say an ArrayList but you can successfully retrieve the same value with an instance of an LinkedList. As both implement the List interface.

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)); 

The above code will output in the console foo.

Here although the implementations are different but if you examine the equals method of ArrayList, it is only checking if the type is a List:

public boolean equals(Object o) { if (o == this) { return true; } if (!(o instanceof List)) { //checking type of super interface return false; } ... } 

The same is true for LinkedList.

Sign up to request clarification or add additional context in comments.

7 Comments

This is a good example of how an answer should be written in StackOverflow - well formulated, informative, with to-the-point code examples. Well done!
This answer is failing to address the "compilation error" aspect of the question and missing the implicit question of why Map.get is declared to accept Object instead of the K parameterized type.
@Miles that question has already been answered here and here.
As per the first link @Moira posted the List example should have the map type be something like Map<ArrayList<String>, String> instead. If the key type is List<String> then V get(K key) will still work and compile fine with both ArrayList and LinkedList. Also this answer does not attempt to answer OP's actual question "Is there a way I can restrict the datatype which I pass as an argument in the get method?"
@SamYonnou with a map defined like this Map<ArrayList<String>, String>, get with a LinkedList<String> instance would still work and retrieve the correct value as in the equals method the instanceof is done w.r.t to List and both ArrayList and LinkedList instances would satisfy that as I mentioned in my answer. And for the actual question many people have already pointed out that it is unnecessary and provided sample code to do it either way. I also explained that with a well defined equals method why it won't be necessary. :)
|
4

I think if it is very important in a project that we control type in HashMap, we could extend HashMap and force using this class instead of HashMap like the below code.

We have all HashMap capabilities, and we should just use the getValue method instead of the get method.

import java.util.HashMap; public class MyHashMap<K,V> extends HashMap<K,V> { public V getValue(K key) { return super.get(key); } } 

Test class:

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

2 Comments

A similar answer was posted maybe an hour ago and was then deleted by its poster...........
I use this approach all the time... i developed this naturally to ensure i can track my map class and not the map implementation. I gives me clarity when navigating the code, but it does not ensure "type safefy"

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.