Chapter: Java The Complete Reference - The Java Language - Generics

Study Material, Lecturing Notes, Assignment, Reference, Wiki description explanation, brief detail

Raw Types and Legacy Code - Java

Because support for generics did not exist prior to JDK 5, it was necessary to provide some transition path from old, pre-generics code.

Raw Types and Legacy Code

 

Because support for generics did not exist prior to JDK 5, it was necessary to provide some transition path from old, pre-generics code. At the time of this writing, there is still pre-generics legacy code that must remain both functional and compatible with generics. Pre-generics code must be able to work with generics, and generic code must be able to work with pre-generics code.

 

To handle the transition to generics, Java allows a generic class to be used without any type arguments. This creates a raw type for the class. This raw type is compatible with legacy code, which has no knowledge of generics. The main drawback to using the raw type is that the type safety of generics is lost.

Here is an example that shows a raw type in action:

 

// Demonstrate a raw type.

class Gen<T> {

 

T ob; // declare an object of type T

 

     //Pass the constructor a reference to an object of type T.

 

Gen(T o) { ob = o;

 

}

 

// Return ob.

T getob() { return ob;

 

}

 

}

 

// Demonstrate raw type.

class RawDemo {

 

public static void main(String args[]) {

 

// Create a Gen object for Integers.

Gen<Integer> iOb = new Gen<Integer>(88);

// Create a Gen object for Strings.

 

Gen<String> strOb = new Gen<String>("Generics Test");

 

     Create a raw-type Gen object and give it

 

     a Double value.

 

Gen raw = new Gen(new Double(98.6));

 

     //Cast here is necessary because type is unknown.

     double d = (Double) raw.getob();

     System.out.println("value: " + d);

 

     //The use of a raw type can lead to run-time exceptions. Here are some examples.

 

     //The following cast causes a run-time error!

 

     int i = (Integer) raw.getob(); // run-time error

 

     //This assignment overrides type safety.

 

strOb = raw; // OK, but potentially wrong

 

     String str = strOb.getob(); // run-time error

 

     //This assignment also overrides type safety.

     raw = iOb; // OK, but potentially wrong

 

     d = (Double) raw.getob(); // run-time error

 

}

 

}

This program contains several interesting things. First, a raw type of the generic Gen class is created by the following declaration:

 

Gen raw = new Gen(new Double(98.6));

 

Notice that no type arguments are specified. In essence, this creates a Gen object whose type T is replaced by Object.

A raw type is not type safe. Thus, a variable of a raw type can be assigned a reference to any type of Gen object. The reverse is also allowed; a variable of a specific Gen type can be assigned a reference to a raw Gen object. However, both operations are potentially unsafe because the type checking mechanism of generics is circumvented.

This lack of type safety is illustrated by the commented-out lines at the end of the program. Let’s examine each case. First, consider the following situation:

 

int i = (Integer) raw.getob(); // run-time error

 

In this statement, the value of ob inside raw is obtained, and this value is cast to Integer. The trouble is that raw contains a Double value, not an integer value. However, this cannot be detected at compile time because the type of raw is unknown. Thus, this statement fails at run time.

 

The next sequence assigns to a strOb (a reference of type Gen<String>) a reference to a raw Gen object:

strOb = raw; // OK, but potentially wrong

//   String str = strOb.getob(); // run-time error

 

The assignment, itself, is syntactically correct, but questionable. Because strOb is of type Gen<String>, it is assumed to contain a String. However, after the assignment, the object referred to by strOb contains a Double. Thus, at run time, when an attempt is made to assign the contents of strOb to str, a run-time error results because strOb now contains a Double. Thus, the assignment of a raw reference to a generic reference bypasses the type-safety mechanism.

 

The following sequence inverts the preceding case:

 

raw = iOb; // OK, but potentially wrong

 

     d = (Double) raw.getob(); // run-time error

 

Here, a generic reference is assigned to a raw reference variable. Although this is syntactically correct, it can lead to problems, as illustrated by the second line. In this case, raw now refers to an object that contains an Integer object, but the cast assumes that it contains a Double. This error cannot be prevented at compile time. Rather, it causes a run-time error.

 

Because of the potential for danger inherent in raw types, javac displays unchecked warnings when a raw type is used in a way that might jeopardize type safety. In the preceding program, these lines generate unchecked warnings:

 

Gen raw = new Gen(new Double(98.6));

 

strOb = raw; // OK, but potentially wrong

 

In the first line, it is the call to the Gen constructor without a type argument that causes the warning. In the second line, it is the assignment of a raw reference to a generic variable that generates the warning.

 

At first, you might think that this line should also generate an unchecked warning, but it does not:

 

raw = iOb; // OK, but potentially wrong

 

No compiler warning is issued because the assignment does not cause any further loss of type safety than had already occurred when raw was created.

One final point: You should limit the use of raw types to those cases in which you must mix legacy code with newer, generic code. Raw types are simply a transitional feature and not something that should be used for new code.

Study Material, Lecturing Notes, Assignment, Reference, Wiki description explanation, brief detail


Copyright © 2018-2020 BrainKart.com; All Rights Reserved. Developed by Therithal info, Chennai.