Generics Safety
Java has always given you the ability to create generalized classes, interfaces, and methods by operating through references of type Object. Because Object is the superclass of all other classes, an Object reference can refer to any type object. Thus, in pre-generics code, generalized classes, interfaces, and methods used Object references to operate on various types of objects. The problem was that they could not do so with type safety.
Generics added the type safety that was lacking. They also streamlined the process, because it is no longer necessary to explicitly employ casts to translate between Object and the type of data that is actually being operated upon. With generics, all casts are automatic and implicit. Thus, generics expanded your ability to reuse code and let you do so safely and easily. For example, consider the following generic class:
Given that the same functionality found in the generic GenClass can be achieved without generics, by simply specifying Object as the data type and employing the proper casts, what is the benefit of making GenClass generic? The answer is that generics automatically ensure the type safety of all operations involving GenClass. In the process, they eliminate the need for you to enter casts and to type-check code by hand. To understand the benefits of generics, first consider the following program that creates a non-generic equivalent of GenClass :
Program
Notice that NonGenClass replaces all uses of T with Object. This makes NonGenClass able to store any type of object, as can the generic version. However, it also prevents the Java compiler from having any real knowledge about the type of data actually stored in NonGenClass, which is bad for two reasons.
1) Explicit casts must be employed to retrieve the stored data. Notice the line :
Because the return type of getob( ) is Object, the cast to Integer is necessary to enable that value to be auto-unboxed and stored in getInt. If you remove the cast, the program will not compile. With the generic version, this cast was implicit. In the non-generic version, the cast must be explicit. This is not only an inconvenience, but also a potential source of error.
2) Many kinds of type mismatch errors cannot be found until run time. Notice the line :
Here, sob is assigned to iob. However, sob refers to an object that contains a string, not an integer. This assignment is syntactically valid because all NonGenClass references are the same, and any NonGenClass reference can refer to any other NonGenClass object. However, the statement is semantically wrong, as the next line shows. Here, the return type of getob( ) is cast to Integer, and then an attempt is made to assign this value to getInt2. The trouble is that iob now refers to an object that stores a String, not an Integer. Unfortunately, without the use of generics, the Java compiler has no way to know this. Instead, a run-time exception occurs when the cast to Integer is attempted. As you know, it is extremely bad to have run-time exceptions occur in your code.
The preceding sequence can’t occur when generics are used. If this sequence were attempted in the generic version of the program, the compiler would catch it and report an error, thus preventing a serious bug that results in a run-time exception. A key point to understand about generic types is that a reference of one specific version of a generic type is not type compatible with another version of the same generic type. For example, the following line of code is in error and will not compile :
Even though both ‘iob’ and ‘sob’ are of type GenClass<T>, they are references to different types because their type parameters differ. This ability to create type-safe code in which type-mismatch errors are caught at compile time is a key advantage of generics. Although using Object references to create “generic” code has always been possible, that code was not type safe, and its misuse could result in run-time exceptions. Generics prevent this from occurring. In essence, through generics, run-time errors are converted into compile-time errors. This is a major advantage.
Program Source
class NonGenClass { Object ob; NonGenClass(Object obj) { ob = obj; } Object getOb() { return ob; } void showType() { System.out.println("T Type : "+ob.getClass().getName()); } } public class Javaapp { public static void main(String[] args) { NonGenClass iob = new NonGenClass(50); iob.showType(); int getInt = (Integer)iob.getOb(); System.out.println("Value : "+getInt); NonGenClass sob = new NonGenClass("hajsoftutorial"); sob.showType(); String getString = (String)sob.getOb(); System.out.println("Value : "+getString); iob = sob; int getInt2 = (Integer)iob; System.out.println("Value : "+getInt2); } }