Skip to main content

Getting rid of unchecked warnings for casts

Posted by emcmanus on March 30, 2007 at 3:27 AM PDT

If you've ever made a serious effort to get rid of "unchecked" warnings from the Java compiler (the ones it gives you with -Xlint:unchecked) then you'll probably have found some cases where you know a cast is correct but you can't convince the compiler of it. Is there anything better than adding @SuppressWarnings("unchecked") around the whole method?

The problem with the @SuppressWarnings solution is that it's much too broad. It suppresses warnings for the whole method, even though it's just one little cast that you want to allow. What if there's another place in the method where the compiler would have given a warning, telling you about a real problem? There's also a readability problem. If it's a big method, it can be hard to work out which particular line is the reason for the @SuppressWarnings annotation; you have to add a comment saying "yoo-hoo! I'm the reason!"

Fortunately, there's a good solution. Suppose your code does this:

    List<String> list = (List<String>) x;

You can change it to this:

    List<String> list = cast(x);

where the cast method is defined like this:

@SuppressWarnings("unchecked")
private static <T> T cast(Object x) {
    return (T) x;
}

If there several different classes where you need this method, you could make it public and put it in a utility class, say Util.java. Then every class that needs it can use import static com.example.mypackage.Util.cast so you can still write just plain cast as above.

The magic here is that the compiler can use
type inference
to figure out what T should be in the method call. In the example, since we're assigning the result of cast to a List, the compiler can infer that T must be List.

The compiler can do this if you're assigning the result of cast to a variable or returning it from a method or using it as a method parameter. In more complicated expressions you might need to break the cast call out into a separate assignment.

In some cases even this won't work. For example, if you want to use the cast method to cast to a type variable you are out of luck:

    <E> Set<E> result(Object x) {
        E e = cast(x);  // does not compile
        return Collections.singleton(e);
    }

You can work around this using the little-known syntax for "explicit type parameters":

    <E> Set<E> result(Object x) {
        E e = Util.<E>cast(x);
        return Collections.singleton(e);
    }

This is ugly, notably because you can't write just <E>cast(x); the explicit type parameter has to appear after a dot. But it shouldn't happen very often.

This cast technique is basically a way to move all of the @SuppressWarnings annotations from your code to single place. You should still follow the same guidelines as you would have without cast. It should only be used as a last resort if you can't restructure your code to eliminate the warnings. And you must be really sure that the value being cast really is of the type you are casting it to. Otherwise you will get a ClassCastException at some completely different part of your code, where there might not even be an explicit cast.

(By the way I'm sure this cast method has been independently invented many times, for example here.)

Related Topics >>

Comments

The cast() method vulnerabilities

In my opinion the cast() method may seem useful, but is actually quite harmful and introduce additional risks to the code. See the post on JavaBlogging about how can the generic cast() method go bad and what can be done about it.