10

When developers interact with non-generic APIs, they usually run into "unchecked" warnings. Consider the following example:

import java.util.AbstractList;

import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

public class IterableNodeList<T extends Node> extends AbstractList<T>
{
    private NodeList list;

    public IterableNodeList(NodeList list)
    {
        this.list = list;
    }

    public T get(int index)
    {
        return (T)this.list.item(index);
    }

    public int size()
    {
        return this.list.getLength();
    }
}

One could of course invest the effort to write this in such a way that there is no warning: using a type parameter T on the class and a constructor argument Class<T>, matching member variable and a cast() call.

Alternatively, one could think about simply editing the IDE configuration and build scripts (e.g. Maven POM) to disable this compiler warning entirely. Now if we did that, the code could stay the way it is, but I'm sure that doing must have drawbacks. However, I can't think of any reasonable, realistic examples where

  • this warning provides more value than "stick on a @SuppressWarnings here, there's no other option anyway", and where
  • the resulting code actually behaves different (and safer) than the one where we ignored (disabled) the warning.

Can you think of such examples or name another reason why disabling these "unchecked" warnings globally is a bad idea? Or is it actually a good idea?

UPDATE

The previous examples did not actually provoke the warnings. Some answers no longer make sense now. Sorry for the inconvenience.

Jens Bannmann
  • 4,845
  • 5
  • 49
  • 76

3 Answers3

5

According to Item 24 of Effective Java 2nd Edition, widely and frequent using @SupressWarnings is generally bad idea, especially if you apply this annotation to the whole class, because such warnings show you possibly dangerous pieces of your code, which could lead to ClassCastException.

But in some case it could be useful, for example in ArrayList's toArray method implementation:

@SuppressWarnings("unchecked")
public <T> T[] toArray(T[] a) {
    if (a.length < size)
        // Make a new array of a's runtime type, but my contents:
        return (T[]) Arrays.copyOf(elementData, size, a.getClass());
    System.arraycopy(elementData, 0, a, 0, size);
    if (a.length > size)
        a[size] = null;
    return a;
}
Andremoniy
  • 34,031
  • 20
  • 135
  • 241
  • The point of my question was to get examples of these "possibly dangerous pieces of code", because I cannot think of one. If we cannot come up with examples where it's good that the compiler issues such warnings, we might disable them using the build scripts / IDE (Maven, eclipse) settings and then don't even have to add @SuppressWarnings everywhere! – Jens Bannmann Feb 05 '13 at 13:57
  • @JensBannmann See my answer [here](http://stackoverflow.com/questions/14464226/how-to-reference-a-generic-return-type-with-multiple-bounds/14469627#14469627) and scroll down to the "note about heap pollution" for an example of dangerous code. – Paul Bellora Feb 05 '13 at 19:46
  • @PaulBellora Okay, that's a good answer - especially the "blew up at the call site instead of the method" part. Could you please post your comment as an answer here, or even copy the "note about heap pollution" section into an answer? Currently my question does not have a straight answer, and I think it might be useful for others if it did. – Jens Bannmann Feb 08 '13 at 13:38
  • @JensBannmann Actually [meriton's answer](http://stackoverflow.com/a/14707768/697449) is very much toward the same point: "the program ... throws a ClassCastException at a line that does not contain a cast in the source code. This is likely to severly confuse most programmers." But I'll see about moving that section into an answer here when I get a chance. This question is actually more appropriate for it. – Paul Bellora Feb 08 '13 at 21:08
  • @PaulBellora: I just stumbled over this one again. Please turn your heap pollution note into an answer here :-) – Jens Bannmann Apr 07 '18 at 06:58
2

Your second example does not result in a compiler warning in my eclipse, nor can I think of a reason why it should. It is therefore my preferred solution.

The reason unchecked warnings exist is that ignoring them can cause heap pollution:

Ordinary casts are checked, i.e. they result in a ClassCastException if the value is not compatible with the desired type. Unchecked casts do not guarantee that, i.e. they can succeed even if the value is not of the proper type. This can result in a variable holding a value that is not a subtype of its declared type, a condition the Java spec calls "heap pollution". To ensure integrity of the runtime type system, the Java compiler will insert ordinary casts whenever a variable of generic type is used. In the presence of heap pollution, these casts can fail.

For instance, the program:

static void appendTo(List list) {
    list.add(1); // unchecked warning
}

static void printLengths(List<String> strings) {
    for (String s : strings) { // throws ClassCastException
        System.out.println(s.length());
    }
}

public static void main(String[] args) throws Exception {
    List<String> strings = new ArrayList<>();
    strings.add("hello");
    appendTo(strings);
    printLengths(strings);
}

throws a ClassCastException at a line that does not contain a cast in the source code. This is likely to severely confuse most programmers.

That is why I recommend to use checked casts whenever possible, either with a non-generic cast, or (in generic code) a reflective cast:

class Habitat<T> {
    private final Class<T> clazz;

    private List<T> inhabitants;

    void add(Object o) {
        inhabitants.add(clazz.cast(o));
    }
}
Paul Bellora
  • 54,340
  • 18
  • 130
  • 181
meriton
  • 68,356
  • 14
  • 108
  • 175
  • I'm not dealing with a typed list in the example, I have made that more obvious now. My second example does provoke the compiler warning, the third one not. But what did we gain in the third example besides making the compiler happy? Why not simply disable the warning in the compiler settings and keep the code like in my second example? – Jens Bannmann Feb 05 '13 at 14:02
  • You're right, the examples don't actually provoke a warning. I will rephrase the question. – Jens Bannmann Feb 05 '13 at 14:07
2

From Effective Java 2nd Edition:

The SuppressWarnings annotation can be used at any granularity, from an individual local variable declaration to an entire class. Always use the SuppressWarnings annotation on the smallest scope possible. Typically this will be a variable declaration or a very short method or constructor. Never use SuppressWarnings on an entire class. Doing so could mask critical warnings.

If you find yourself using the SuppressWarnings annotation on a method or constructor that’s more than one line long, you may be able to move it onto a local variable declaration. You may have to declare a new local variable, but it’s worth it.

It is illegal to put a SuppressWarnings annotation on the return statement, because it isn’t a declaration [JLS, 9.7]. You might be tempted to put the annotation on the entire method, but don’t. Instead, declare a local variable to hold the return value and annotate its declaration

Natix
  • 14,017
  • 7
  • 54
  • 69