Some Restrictions on Wildcard Types – Generics

Some Restrictions on Wildcard Types

Wildcards cannot be used in instance creation expressions:

Click here to view code image

Node<?> anyNode = new Node<?>(2020, null);                 // Compile-time error!
Node<? extends Integer> extIntNodeA
               = new Node<? extends Integer>(0, null);     // Compile-time error!
Node<? extends Integer> extIntNodeB = new Node<Integer>(0, null);  // OK

The actual type parameter in the constructor call must be a concrete type. Creating instances of wildcard parameterized types is analogous to instantiating interface types; neither can be used in object creation expressions.

Wildcards cannot be used in the header of reference type declarations. Supertypes in the extends and implements clauses cannot have wildcards.

Click here to view code image

class QuestionableNode<?>  { /* … */ }                             // Not OK.
class SubNode extends Node<?> { /* … */ }                          // Not OK.
interface INode extends Comparable<? extends Node<?>> { /* … */ }  // Not OK.
class XNode implements Comparable<?>  { /* … */ }                  // Not OK.

However, nested wildcards are not a problem in a reference type declaration header or in an object creation expression:

Click here to view code image

class OddNode extends Node<Node<?>> implements Comparable<Node<?>> { /* … */ }

Node<?> nodeOfAnyNode = new Node<Node<?>>(new Node<Integer>(2020, null), null);

11.5 Using References of Wildcard Parameterized Types

A wildcard type can be used to declare parameterized references—that is, references whose type is a wildcard parameterized type. In this section, we look at how such references are used in the following contexts:

  • Assignment between such references
  • Calling methods of generic types using such references

The generic class Node<E> used in this subsection is defined in Example 11.2, p. 568.

Generic Reference Assignment

A reference of a supertype can refer to an object of a subtype, and this substitution principle applies to parameterized types as well. Assignment compatibility is according to the type hierarchy of the parameterized types. Figure 11.5 shows partial type hierarchy for selected parameterized types of the generic class Node<E>. It combines the type hierarchies from Figure 11.3 and Figure 11.4. As we would expect, widening reference conversions according to the type hierarchy are always type-safe. All but the last assignment statement in the code below are legal. The types Node<Number> and Node<Integer> are unrelated. (The notation B <: A means B is a subtype of A.)

Click here to view code image

Node<Object>  objNode = new Node<Object>(100, null);
Node<Number>  numNode = new Node<Number>(200, null);
Node<Integer> intNode = new Node<Integer>(300, null);
Node<? extends Number> extNumNode
                       = intNode; // Node<Integer> <: Node<? extends Number>
Node<? super Integer>  supIntNode
                       = numNode; // Node<Number> <: Node<? super Integer>
supIntNode = objNode;             // Node<Object> <: Node<? super Integer>
numNode    = intNode;             // Compile-time error! Types unrelated.

In the code below, we get an error at (1) because the types Node<? extends Number> and Node<? super Number> are unrelated, but that is not the case for the types Node<? extends Object> and Node<? super Object> at (2). The family of types denoted by the type Node<? super Object> has the subtype Node<Object> only, which is also a subtype of the type Node<? extends Object>. In the assignment at (3), the type Node<? extends Object> is not a subtype of the type Node<? super Object>, but the converse is true as established at (2).

Click here to view code image

Node<? super Number>   supNumNode;
Node<? extends Object> extObjNode;
Node<? super Object>   supObjNode;
extNumNode = supNumNode;  // (1) Compile-time error! Types unrelated.
extObjNode = supObjNode;  // (2) Node<? super Object> <: Node<? extends Object>
supObjNode = extObjNode;  // (3) Compile-time error!

Narrowing reference conversion requires an explicit cast, except for the cases noted below (see also Figure 11.5). The raw type Node and the unbounded wildcard parameterized type Node<?> are essentially equivalent in this regard. Conversion between the two is type-safe:

Click here to view code image

Node    rawNode;
Node<?> anyNode;
rawNode = anyNode;  // Node <– Node<?> is type-safe.
anyNode = rawNode;  // Node<?> <– Node is type-safe.

The unbounded wildcard parameterized type Node<?> and the upper bounded wildcard parameterized type Node<? extends Object> are also essentially equivalent (see (4)), except when assigned a value of the raw type Node (see (5)).

Click here to view code image

// (4):
anyNode = extObjNode;  // Node<?> <– Node<? extends Object> is type-safe.
extObjNode = anyNode;  // Node<? extends Object> <– Node<?> is type-safe.

// (5):
anyNode    = rawNode;  // Node<?> <– Node is type-safe.
extObjNode = rawNode;  // Node<? extends Object> <– Node: Unchecked Conversion

Assigning a value of the raw type Node to a reference of the type Node<? extends Object> results in an unchecked conversion warning—which conforms to the general rule when mixing legacy and generic code: Assigning the value of a raw type to a reference of a bounded wildcard parameterized type or a concrete parameterized type results in an unchecked conversion warning, as illustrated by the examples below.

Click here to view code image

extNumNode = rawNode; // Node<? extends Number> <– Node: Unchecked Conversion
intNode    = rawNode; // Node<Integer> <– Node: Unchecked Conversion

For a discussion of explicit casting of parameterized references, see §11.13, p. 625. Suppressing different kinds of unchecked warnings with the @SuppressWarnings(“unchecked”) annotation is discussed in §11.13, p. 623, and §25.5, p. 1582.

Leave a Reply

Your email address will not be published. Required fields are marked *