Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Casting from Object in Java without getting an unchecked warning

I wrote a class that has a map of <String, Object>. I need it to hold arbitrary objects, but at the same time sometimes I need to cast some of those objects, so I'll do something like

HashMap<String, Object> map = new HashMap<String, Object>();                                                                                 
Object foo = map.get("bar");                                                                                                                                                                                                         
if (foo instanceof HashMap) {                                                                                                                                                                                                        
    ((HashMap<String, Integer>) foo).put("a", 5);                                                                                                                                                                                    
}            

which gives the warning

Stuff.java:10: warning: [unchecked] unchecked cast
found   : java.lang.Object
required: java.util.HashMap<java.lang.String,java.lang.Integer>
        ((HashMap<String, Integer>) foo).put("a", 5);

I suspect it has to do with the use of generics. I can get rid of the error using @SupressWarnings("unchecked"), but I was wondering if there was a better way to do it. Or maybe the fact that I'm getting the warning means I should reconsider what I'm doing. Is there anything I could do, or should I just use @SupressWarnings?

like image 804
swampsjohn Avatar asked Nov 21 '09 02:11

swampsjohn


People also ask

How do you get rid of warnings in Java?

Use of @SuppressWarnings is to suppress or ignore warnings coming from the compiler, i.e., the compiler will ignore warnings if any for that piece of code. 1. @SuppressWarnings("unchecked") public class Calculator { } - Here, it will ignore all unchecked warnings coming from that class.

What is an unchecked cast in Java?

Unchecked cast means that you are (implicitly or explicitly) casting from a generic type to a nonqualified type or the other way around.

What are unchecked warnings in Java?

An unchecked warning tells a programmer that a cast may cause a program to throw an exception somewhere else. Suppressing the warning with @SuppressWarnings("unchecked") tells the compiler that the programmer believes the code to be safe and won't cause unexpected exceptions.


2 Answers

Edited (based on question clarification)

Casting to HashMap<String, Integer> (btw, using Map instead of HashMap is arguably a better choice) is a different story. There's sadly no way to avoid an unchecked warning in that case due to type erasure. You can, however, use it as non-generic map:

if (foo instanceof Map) {                                                                                                                                                                                                        
  ((Map) foo).put("a", 5);                                                                                                                                                                                    
}

You'll obviously have to cast on "gets" and you lose (perceived) type safety but there'll be no unchecked warning.


There must be more to this story. The following code:

Map<String, Object> map = Maps.newHashMap(); // or new HashMap<String, Object>();
Object foo = map.get("bar");
if (foo instanceof Widget) {
  ((Widget) foo).spin();
}

does NOT generate an unchecked warning for me. Nor can I imagine why would it. If you know beforehand that "bar" would always return a widget, doing this:

Widget widget = (Widget) map.get("bar");
widget.spin();

would work perfectly fine as well. Am I missing something here?

like image 52
ChssPly76 Avatar answered Oct 23 '22 13:10

ChssPly76


If everything else (polymorphic implementation, casts) is not applicable you can implement a heterogeneous container as described in Item 33: Consider type-safe heterogeneous containers in "Effective Java", 3rd Edition. The responsibility of the container is to ensure type-safeness.

public class Container{
  private Map<Class<?>, Object> favorites = new HashMap<Class<?>, Object>();
  public <T> void set(Class<T> klass, T thing) {
    favorites.put(klass, thing);
  }
  public <T> T get(Class<T> klass) {
    return klass.cast(favorites.get(klass));
  }
}

The problem with your example is that you're using a HashMap<K,V> as an entry type. This cannot be represented with a class literal as a type token. So you have to implement some form of super type token:

public abstract class TypeReference<T> {}

Your client code would then extend TypeReference for every type token needed:

TypeReference<?> typeToken = new TypeReference<HashMap<String, Integer>>{};

The type information is accessible at run-time. The container implementation has then to type check against the actual type parameters of of the type token (subclass of TypeReference).

This is a complete solution but a lot of work to implement. No collection library I know of does support containers with type references.

like image 23
Thomas Jung Avatar answered Oct 23 '22 12:10

Thomas Jung