26

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();
    }
}
5
  • Do you mean, how to remove unchecked cast warning you get? Commented Oct 3, 2013 at 15:48
  • You can use if (obj instanceof String) { //your code } Commented Oct 3, 2013 at 15:52
  • @Sarma The object is a HashMap<String,String> not String Commented Oct 3, 2013 at 15:53
  • U can type cast HashMap hashMap = (HashMap) object;It will work with warnings. Commented Oct 3, 2013 at 15:58
  • This solution simplifies a bit, but doesn't solve a problem of instanceof Map<smth> in Java. Java has lack of this functionality. Commented May 24, 2019 at 8:40

5 Answers 5

28

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.

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

Comments

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

Comments

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

Comments

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.

6 Comments

Have you tried with your code? ClassCastExp throw when you call addHelper method.
this is a generic solution showing how the type handling works, its trivial to put it in addHelper
This is not generic solution. Please, run your code, than you will see where exception is thrown.
So where will the exception be thrown, and why?
it gets thrown on a mismatch of method to object/calling map methods on a non-map object
|
0

Java 14 introduced support for pattern matching (Preview) for the instanceof operator.
And from java 16 as full support JEP 394.
Now you can achieve this by doing that like this:

if (object instanceof Map myMap
    && myMap.get("foo") instanceof String myFoo
    && myMap.get("bar") instanceof String myBar) {

    this.foo = myFoo;
    this.bar = myBar;
}

Some notes:

  • If your map declared as Map<String, String> then there no need to check the second check for "bar", on this example i did it only to match the question asked.
  • If your map declared as Map<String, Object> then it's better to check all the options as i did in my example.
  • If for some reason you don't have the key for one of the values in the map but the values are from the same type!

you can do this:

if (result instanceof Map myMap
    && !myMap.isEmpty()
    && myMap.values().stream().findFirst().get() instanceof String value) { // or any other type you need to check... 
 //do something...
}  

Hope this will help someone in the future...

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.