Parameterized Types – Generics

Parameterized Types

A parameterized type (also called a type instance) is an invocation or instantiation of a generic type that is a specific usage of the generic type where the formal type parameters are replaced by actual type parameters. Analogy with method declarations and method invocations can be helpful in understanding the relationship between generic types and parameterized types. We pass actual parameters in a method invocation to execute a method. In the case of a generic type invocation, we pass actual type parameters in order to instantiate a generic type.

We can declare references and create objects of parameterized types, and call methods on these objects, in much the same way as we use non-generic classes.

Click here to view code image

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

The actual type parameter Integer, explicitly specified in the declaration statement above, binds to the formal type parameter E in Example 11.2. The compiler treats the parameterized type Node<Integer> as a new type. The parameterized type Node<Integer> constrains the generic type Node<E> to Integer objects, thus implementing homogenous nodes with Integers. The reference intNode can only refer to a Node of Integer. The node created can only be used to store an object of this concrete type.

Methods can be called on objects of parameterized types:

Click here to view code image

Integer iRef = intNode.getData();            // Integer object with int value 2020

In the method call above, the actual type parameter is determined from the type of the reference used to make the call. The type of the intNode reference is Node<Integer>; therefore, the actual type parameter is Integer. The method header is Integer getData(), meaning that the method will return a value of type Integer. The compiler checks that the return value can be assigned. As the compiler guarantees that the return value will be an Integer and can be assigned, no explicit cast or runtime check is necessary. The compiler actually inserts the necessary cast. Here are some more examples of calling methods of parameterized types:

Click here to view code image

intNode.setData(2020);                             // Ok.
intNode.setData(“TwentyTwenty”);                   // (1) Compile-time error!
intNode.setNext(new Node<Integer>(2019, null));    // (2020, (2019, null))
intNode.setNext(new Node<String>(“Hi”, null));     // (2) Compile-time error!

In the method calls shown above, the compiler determines that the actual type parameter is Integer. The method signatures are setData(Integer) and setNext(Node<Integer>). As expected, we get a compile-time error when we try to pass an argument that is not compatible with the parameter type in the method declarations; for example, at (1) and (2). The parameterized types Node<Integer> and Node<String> are two unrelated types. The compiler reports any inconsistent use of a parameterized type so that errors can be caught earlier at compile time and the use of explicit casts in the source code is minimized, as evident from (3) and (4), respectively.

Click here to view code image

Node<String> strNode = new Node<String>(“Hi”, null);
intNode = strNode;                         // (3) Compile-time error!
String str = strNode.getData();            // (4) No explicit cast necessary.

Leave a Reply

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