Search code examples
javagenericswildcardraw-types

situations where an error will be caught by using unbounded wildcards, but will only be flagged as a warning if you use raw type


Below is a quote on comparison betweenunbounded wildcard types vs raw types, from Java generics and collection by Maurice Naftalin and Philip Wadler.

We recommend using unbounded wildcard types in preference to raw types because they provide stronger static typing guarantees; many mistakes that are caught as an error when you use unbounded wildcards will only be flagged as a warning if you use raw types.

But the book doesn't provide an code example to explain the situation. I was wondering if someone could add to the explanation by providing an code example?


Solution

  • Well, you could adapt some of the examples of that same chapter to reproduce a case of this.

    For example, consider the following generic class:

    class Node<E> {
       private E value;
       @Override public String toString(){ return value.toString(); }
    }
    

    Now, imagine that you write the following piece of code, which is wrong:

    Object o = new Node<Integer>(10);
    Node<String> node = new Node<>("Hello");
    if(o instanceof Node) {
      Node other = (Node) o;
      other.value = node.value; //Uh oh! Warning
    }
    System.out.println(node); //Hello
    System.out.println(other); //Hello - WTH!
    

    If you try to compile this, you would only get a warning, but it would still compile just fine:

    javac -Xlint:unchecked Node.java
    
    Node.java:21: warning: [unchecked] unchecked assignment to variable value as member of raw type Node
                other.value = node.value;
                     ^
    1 warning
    

    However, if you change the code to use unbounded wildcards:

    Object o = new Node<Integer>(10);
    Node<String> node = new Node<>("Hello");
    if(o instanceof Node<?>) {
      Node<?> other = (Node<?>) o;
      other.value = node.value; //Uh oh! Compiler error
    }
    

    Now you would get the following error when compiling:

    javac -Xlint:unchecked Node.java
    
    Node.java:21: error: incompatible types: String cannot be converted to CAP#1
                other.value = node.value;
                                  ^
      where CAP#1 is a fresh type-variable:
        CAP#1 extends Object from capture of ?
    1 error
    

    So, as you can see the unbounded wildcard offers better type checking guarantees than the raw type.