Implications for Casting
A non-reifiable type can lose important type information during erasure and the cast may not have the desired effect at runtime. A cast to a non-reifiable type is generally flagged as an unchecked cast warning, and the cast is replaced by a cast to its erasure. Again, the compiler permits casts to allow interoperability between legacy code and generic code—usually with a warning.
The following code shows why a warning is necessary. The reference value of a Number node, declared at (1), is assigned to a reference of type Node<?> at (2). This reference is cast to a Node<String> and its reference value is assigned to a reference of type Node<String> at (3). A String is set as data in the node at (4). The data is retrieved from the node via the numNode reference and assigned to a Number reference at (5).
Node<Number> numNode = new Node<>(20, null); // (1)
Node<?> anyNode = numNode; // (2)
Node<String> strNode = (Node<String>) anyNode; // (3) Unchecked cast warning
strNode.setData(“Peekaboo”); // (4)
Number num = numNode.getData(); // (5) ClassCastException
The erasure of the assignment at (3) is equivalent to the following assignment, with the cast succeeding at runtime:
Node strNode = (Node) anyNode; // (3′)
However, a ClassCastException occurs at (5) because a String cannot be assigned to a Number. The compiler warns of potential problems by issuing an unchecked cast warning at (3).
The types Node<String> and Node<Number> are unrelated. That is the reason why the Number node in the above example was compromised by going through a node of type Node<?>. As we would expect, a cast between unrelated types results in a compile-time error:
strNode = (Node<String>) numNode; // Compile-time error
If we are casting a generic supertype to a generic subtype, where the parameterization is identical, the cast is safe and no warning is issued:
// BiNode<E> is a subtype of MonoNode<E>.
MonoNode<String> monoStrNode = new BiNode<>(“Hi”, null, null);
BiNode<String> biStrNode = (BiNode<String>) monoStrNode; // Ok. No warning.
The method castaway() below shows examples of casting an Object reference that refers to a node of type String, declared at (2).
//@SuppressWarnings(“unchecked”) // (1) Suppress warnings at (4),(6),(7).
public static void castaway() {
Object obj = new Node<>(“one”, null); // (2)
Node<String> node1 = obj; // (3) Compile-time error!
Node<String> node2 = (Node<String>) obj; // (4) Unchecked cast
Node<String> node3 = (Node<?>) obj; // (5) Compile-time error!
Node<String> node4 = (Node<String>)(Node<?>) obj; // (6) Unchecked cast
Node<String> node5 = (Node) obj; // (7) Unchecked conversion
Node<?> node6 = (Node) obj; // (8) OK.
Node<?> node7 = (Node<?>)obj; // (9) OK.
}
It is instructive to see what warnings and errors are issued by the compiler. The compile-time error at (3) is due to incompatible types: An Object cannot be assigned to a Node<String> reference. The compiler issues an unchecked cast warning at (4) because of the cast from an Object to the concrete parameterized type Node<String>. The compile-time error at (5) is due to incompatible types: A Node<?> cannot be assigned to a Node<String> reference. There are two casts at (6): An Object is cast to Node<?>, which in turn is cast to Node<String>. The cast to Node<?> is permitted, but the second cast results in an unchecked cast warning. The compiler issues an unchecked conversion warning at (7), since a raw type (Node) is being assigned to a parameterized type (Node<String>). (8) and (9) show that casting to the raw type or to the unbounded wildcard is always permitted, since both types are reifiable.
If the annotation @SuppressWarnings(“unchecked”) at (1) is uncommented, the unchecked warnings at (4), (6), and (7) in the method castaway() will be suppressed (§25.5, p. 1582). Use of this annotation is recommended when we know that unchecked cast warnings are inevitable in a language construct (a type declaration, a field, a method, a parameter, a constructor, a local variable). Any unchecked warnings reported by the compiler are those that were not documented using this annotation. The use of an unbounded wildcard is recommended in casts, rather than using raw types, as it provides for stricter type checking.
Leave a Reply