23

I have a method that accepts an Object. In one use case, the method accepts a HashMap<String, String> and sets each value to the property of the corresponding key name.

public void addHelper(Object object) {
    if (object instanceof HashMap) {
        HashMap<String, String> hashMap = (HashMap<String, String>) object;
        this.foo = hashMap.get("foo");
        this.bar = hashMap.get("bar");
    }
}

This class adheres to a particular interface, so adding setters for those properties is not an option.

My question is, how can I check the type cast here?

HashMap<String, String> hashMap = (HashMap<String, String>) object;

Thanks in advance!

SOLUTION

Thanks to the answer from @drobert, here is my updated code:

public void addHelper(Object object) {
    if (object instanceof Map) {
        Map map = (Map) object;
        if (map.containsKey("foo")) this.foo = map.get("foo").toString();
        if (map.containsKey("bar")) this.bar = map.get("bar").toString();
    }
}
Shaun Scovil
  • 3,905
  • 5
  • 39
  • 58

4 Answers4

26

You can't. Due to type erasure, reflection will show you have an instance of HashMap, but the types are dropped at runtime. Effectively, you have HashMap< Object,Object >.

That said, you still have some options, and some advice I'd suggest you take. Among them:

  • Check to see if it's an instance of 'Map' rather than 'HashMap'. It will make your API much more flexible since you most likely only care that you have rapid access by key rather than any particular impementation
  • Take advantage of java.util.Map's api which defines 'containsKey(Object)' and 'get(Object)', so you can still use mapInst.get("stringKey") safely, even without having to cast.
  • You can't ensure all values are strings, but you can take advantage of java.lang.Object's toString() method and obtain a String for each value regardless.

In short: treat this like any Map, and attempt to access the keys as Strings even without the cast, and attempt to utilize each value as a String by doing a null check then calling .toString() and you'll have a much safer implementation.

drobert
  • 1,230
  • 8
  • 21
8

It should be noted that the original routine is exactly equivalent to coding

public void addHelper(Object object) {
    if (object instanceof HashMap) {
        HashMap hashMap = (HashMap) object;
        this.foo = (String)(hashMap.get("foo"));
        this.bar = (String)(hashMap.get("bar"));
    }
}

The explicit (HashMap) cast cannot possibly throw an error, since it's guarded by the instanceof. The implicitly-supplied (String) casts will only throw an error if the values returned from the HashMap are not Strings (or nulls).

(By "exactly equivalent to" I mean that the same bytecode is produced.)

Hot Licks
  • 47,103
  • 17
  • 93
  • 151
2

You should use try-catch, where you call addHelper(Object) method. This will ensure your correct type of HashMap.

      try{
        addHelper(hashMap);
        }
        catch(ClassCastException ex){
            System.out.println("Is not desired hashmap");
        }
Masudul
  • 21,823
  • 5
  • 43
  • 58
0
   try{
    HashMap<String, String> hashMap = (HashMap<String, String>) object;
    this.foo = hashMap.get("foo");
    this.bar = hashMap.get("bar");
    }catch(ClassCastException e){
        System.err.log("Object is not a hashmap");
    }

Now, you know the object is of the correct type - even a custom abstract class or otherwise.

roguequery
  • 964
  • 13
  • 28
  • Have you tried with your code? ClassCastExp throw when you call addHelper method. – Masudul Oct 03 '13 at 15:57
  • this is a generic solution showing how the type handling works, its trivial to put it in addHelper – roguequery Oct 03 '13 at 15:59
  • This is not generic solution. Please, run your code, than you will see where exception is thrown. – Masudul Oct 03 '13 at 16:01
  • So where will the exception be thrown, and why? – Hot Licks Oct 03 '13 at 16:17
  • it gets thrown on a mismatch of method to object/calling map methods on a non-map object – roguequery Oct 03 '13 at 16:20
  • It will get thrown due to the hidden cast generated on the results of hashMap.get, as it's cast from Object to String. If indeed the result is not a String. If the result is a String then no exception is thrown. The cast to HashMap will throw an error only if "object" is not a HashMap at all, and that is probably better checked with `instanceof` rather than waiting for the exception. – Hot Licks Oct 03 '13 at 17:28