Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unsafe generic cast when deserializing a Collection

public Configuration(Node node, File file) {
    HashMap<String, String> conf = (HashMap<String, String>) SerializationUtils.deserialize(new FileInputStream(file));
}

I understand why this gives an unsafe cast warning, but what's the best/accepted way to do this safely? Is there any good way?

like image 875
Steven Schlansker Avatar asked Oct 22 '09 21:10

Steven Schlansker


3 Answers

You cannot handle this situation in an entirely type-safe way using only the Java language.

Because this is something that has to be done repeatedly and you can't really get around it, I suggest using a genreic method to and to read and cast generic objects:

@SuppressWarnings("unchecked")
public static <T> T readObject(
    ObjectInputStream in
) throws IOException, ClassNotFoundException {
    return (T)in.readObject();
}

However, I suggest that you don't generally use methods like this to suppress valid warnings.

like image 104
Tom Hawtin - tackline Avatar answered Oct 21 '22 00:10

Tom Hawtin - tackline


There's not really any way of doing this properly because the compile-time type information you want to check (i.e. String) is not available at runtime, (i.e. when the cast actually occurs) through the process known as erasure. I think that the best way is for you to pass your deserialized collection thru some bespoke "checker":

Map<?,?> conf = deserialize(rsrc);
Map<String, String> checked = checkMap(conf, String.class, String.class);
//can use checked freely

where:

@SuppressWarnings("unchecked")
public static <K, V> Map<K,V> checkMap(Map<?,?> map, Class<? extends K> k, Class<? extends V> v) {
    for (Map.Entry<?, ?> e : map) {
        k.cast(e.getKey());   //will throw ClassCastException
        v.cast(e.getValue());
    }
    return (Map<K,V>) map; //unchecked 
}
like image 34
oxbow_lakes Avatar answered Oct 21 '22 00:10

oxbow_lakes


To build on the earlier answer, I usually go a little further when suppressing warnings. I put the annotation on a local variable instead of the method, to reduce the scope of the suppression. This means that if anyone comes along an augments the method later, there will not be an unintentional suppression. It does add another line of code, but I think the trade off is worth it.

public static <T> T readObject(
    ObjectInputStream in
) throws IOException, ClassNotFoundException {
    @SuppressWarnings("unchecked")
    T val = (T)in.readObject();
    return val;
}

Unfortunately you cannot add an annotation to an expression (at least not yet).

like image 1
dstine Avatar answered Oct 20 '22 23:10

dstine