How do I address unchecked cast warnings?

后端 未结 23 1200
醉梦人生
醉梦人生 2020-11-22 03:06

Eclipse is giving me a warning of the following form:

Type safety: Unchecked cast from Object to HashMap

This is from a call to

相关标签:
23条回答
  • 2020-11-22 03:53

    Almost every problem in Computer Science can be solved by adding a level of indirection*, or something.

    So introduce a non-generic object that is of a higher-level that a Map. With no context it isn't going to look very convincing, but anyway:

    public final class Items implements java.io.Serializable {
        private static final long serialVersionUID = 1L;
        private Map<String,String> map;
        public Items(Map<String,String> map) {
            this.map = New.immutableMap(map);
        }
        public Map<String,String> getMap() {
            return map;
        }
        @Override public String toString() {
            return map.toString();
        }
    }
    
    public final class New {
        public static <K,V> Map<K,V> immutableMap(
            Map<? extends K, ? extends V> original
        ) {
            // ... optimise as you wish...
            return Collections.unmodifiableMap(
                new HashMap<String,String>(original)
            );
        }
    }
    
    static Map<String, String> getItems(HttpSession session) {
        Items items = (Items)
            session.getAttribute("attributeKey");
        return items.getMap();
    }
    

    *Except too many levels of indirection.

    0 讨论(0)
  • 2020-11-22 03:56

    Wow; I think I figured out the answer to my own question. I'm just not sure it's worth it! :)

    The problem is the cast isn't checked. So, you have to check it yourself. You can't just check a parameterized type with instanceof, because the parameterized type information is unavailable at runtime, having been erased at compile time.

    But, you can perform a check on each and every item in the hash, with instanceof, and in doing so, you can construct a new hash that is type-safe. And you won't provoke any warnings.

    Thanks to mmyers and Esko Luontola, I've parameterized the code I originally wrote here, so it can be wrapped up in a utility class somewhere and used for any parameterized HashMap. If you want to understand it better and aren't very familiar with generics, I encourage viewing the edit history of this answer.

    public static <K, V> HashMap<K, V> castHash(HashMap input,
                                                Class<K> keyClass,
                                                Class<V> valueClass) {
      HashMap<K, V> output = new HashMap<K, V>();
      if (input == null)
          return output;
      for (Object key: input.keySet().toArray()) {
        if ((key == null) || (keyClass.isAssignableFrom(key.getClass()))) {
            Object value = input.get(key);
            if ((value == null) || (valueClass.isAssignableFrom(value.getClass()))) {
                K k = keyClass.cast(key);
                V v = valueClass.cast(value);
                output.put(k, v);
            } else {
                throw new AssertionError(
                    "Cannot cast to HashMap<"+ keyClass.getSimpleName()
                    +", "+ valueClass.getSimpleName() +">"
                    +", value "+ value +" is not a "+ valueClass.getSimpleName()
                );
            }
        } else {
            throw new AssertionError(
                "Cannot cast to HashMap<"+ keyClass.getSimpleName()
                +", "+ valueClass.getSimpleName() +">"
                +", key "+ key +" is not a " + keyClass.getSimpleName()
            );
        }
      }
      return output;
    }
    

    That's a lot of work, possibly for very little reward... I'm not sure if I'll use it or not. I'd appreciate any comments as to whether people think it's worth it or not. Also, I'd appreciate improvement suggestions: is there something better I can do besides throw AssertionErrors? Is there something better I could throw? Should I make it a checked Exception?

    0 讨论(0)
  • 2020-11-22 03:56

    If you are sure that the type returned by session.getAttribute() is HashMap then you can not typecast to that exact type, but rely on only checking the generic HashMap

    HashMap<?,?> getItems(javax.servlet.http.HttpSession session) {  
        HashMap<?,?> theHash = (HashMap<?,?>)session.getAttribute("attributeKey");
        return theHash;
    } 
    

    Eclipse will then surprise warnings, but of course this can lead to runtime errors that can be hard to debug. I use this approach in not operation-critical contexts only.

    0 讨论(0)
  • 2020-11-22 03:58

    In the HTTP Session world you can't really avoid the cast, since the API is written that way (takes and returns only Object).

    With a little bit of work you can easily avoid the unchecked cast, 'though. This means that it will turn into a traditional cast giving a ClassCastException right there in the event of an error). An unchecked exception could turn into a CCE at any point later on instead of the point of the cast (that's the reason why it's a separate warning).

    Replace the HashMap with a dedicated class:

    import java.util.AbstractMap;
    import java.util.Collection;
    import java.util.HashMap;
    import java.util.Map;
    import java.util.Set;
    
    public class Attributes extends AbstractMap<String, String> {
        final Map<String, String> content = new HashMap<String, String>();
    
        @Override
        public Set<Map.Entry<String, String>> entrySet() {
            return content.entrySet();
        }
    
        @Override
        public Set<String> keySet() {
            return content.keySet();
        }
    
        @Override
        public Collection<String> values() {
            return content.values();
        }
    
        @Override
        public String put(final String key, final String value) {
            return content.put(key, value);
        }
    }
    

    Then cast to that class instead of Map<String,String> and everything will be checked at the exact place where you write your code. No unexpected ClassCastExceptions later on.

    0 讨论(0)
  • 2020-11-22 03:58

    Solution: Disable this warning in Eclipse. Don't @SuppressWarnings it, just disable it completely.

    Several of the "solutions" presented above are way out of line, making code unreadable for the sake of suppressing a silly warning.

    0 讨论(0)
提交回复
热议问题