views:

174

answers:

3
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?

+1  A: 

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.

Tom Hawtin - tackline
That's what I was afraid of; was just hoping that I was missing something :/
Steven Schlansker
It's a valid warning only in context of a broken language feature design!
Jonathan Feinberg
If it wasn't for type erasure, we wouldn't be able to deserialise these objects. I think it's a win.
Tom Hawtin - tackline
A: 

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 
}
oxbow_lakes
Anyone care to elucidate as to why a perfectly valid and correct answer has been downvoted? Is it because *I* downvoted an invalid and incorrect answer by any chance?
oxbow_lakes
I wasn't the downvoter, but I can see that this may not work in the case of, say, multithreaded access. One thread runs the checker, while another uses the original reference to insert the "wrong" type after the check. Boom, ClassCastException.
Steven Schlansker
@Steven - I don't think *"might be unsafe in a multi-threaded environment"* is necessarily a fault with this `check` approach. I've added a bit more detail to my answer. The method is simply not designed to work in this manner and could be documented thusly
oxbow_lakes
+1 for counterbalance.
Andrzej Doyle
A: 

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

dstine
I tend towards putting the annotation on the method, because the entire point of the method is to suppress a warning.
Tom Hawtin - tackline