Register Now

Login

Lost Password

Enter your email to reset your password.

BY Author

Java – Generics

First, let’s see what generic means.

Here generics means common/not specific.

In our day to day life, we use many things generically.

It is like, something that is designed/built once and can be used several times in common by anyone (independently of any specific type).

For example, we can use the same house for different members of our family irrespective of their gender and age. There is no need to build a separate house for each member of the family because the same house can fulfil all the needs of all family members without any problem.

Java Programming Language Generics

Similarly, in Java, generics concept allows us to use the same piece of code several times in common for different reference types.

That is using generics we can use a single classconstructormethod or interface that can work with different types of data.

Note

Generics work only with different reference types and not with primitive types such as intfloatdoublechar, etc.

Generics not only helps us to use same code several times with different reference types but also provide type safety & resolve type casting problems. It will be explained later in detail with few examples.

Now let’s see with practical examples why generics concept is so important and how it works?

Sometimes two or more algorithms have the same logic.

For example, consider the example given below. There are two input methods and two output methods with the same logic.
Even though algorithm logic of two input methods is same, we need to overload input methods.
Even though algorithm logic of two output methods is same, we need to write two different output methods.
This increases the code unnecessarily even though their algorithm is logically same.

Example

 public class Test 
 {
     Integer var1;
     Double var2;
	
     public void input(Integer var1)
     {
         this.var1 = var1;
     }
	
     public void input(Double var2)
     {
         this.var2 = var2;
     }
	
     public Integer output1()
     {
         return var1;
     }
	
     public Double output2()
     {
         return var2;
     }
	
     public static void main(String args[])
     {
         Test t1 = new Test();
         Test t2 = new Test();
		
         t1.input(90);
         t2.input(99.99);
		
         int i = t1.output1();
         double d = t2.output2();
		
         System.out.println(i);
         System.out.println(d);
     }
 }

Output

90
99.99

But Generics helped us to use the same code for the algorithms those are logically same.
So let’s see how generics will help to reduce above code which is logically same, through the example given below:

 public class GenericTest<T>; 
 {
     T var;
	
     public void input(T var)
     {
         this.var = var;
     }
	
     public T output()
     {
         return var;
     }
	
     public static void main(String args[])
     {
         GenericTest<Integer> gt1 = new GenericTest<Integer>();
         GenericTest<Double> gt2 = new GenericTest<Double>();
		
         gt1.input(90);
         gt2.input(99.99);
		
         int i = gt1.output();
         double d = gt2.output();
		
         System.out.println(i);
         System.out.println(d);
     }
 }

Output

90
99.99

Now you can see, in the above program with the help of generics, we can achieve the same output as that of the first program by using only one instance variable, one input method & one output method. This is how generics concept helps us to use the same code for different reference types.

You need not worry about working of above generics example because we are going to explain it in detail.

Look at the starting line,

public class GenericTest<T>

in the above program.

T is placed within <>. This T acts as a substitute for the reference type that is passed to the class GenericTest when an object is created.

For example, in the above program reference type is passed at the line,

GenericTest<Integer> gt1 = new GenericTest<Integer>();

                                                                     And

GenericTest<Double> gt2 = new GenericTest<Double>();

At line,

GenericTest<Integer> gt1 = new GenericTest<Integer>();

Reference of Integer type is passed to class GenericTest, and it takes the place of T at runtime wherever it is located.

Let’s see how it happens, through diagram shown below:

Java Programming Language Generics Example 1

Java compiler removes all the T’s and replaces them by Integer. Thus, the program behaves as if it already contains Integer type wherever T is located as shown below:

 public class GenericTest 
 {
     Integer var;
	
     public void input(Integer var)
     {
         this.var = var;
     }
	
     public Integer output()
     {
         return var;
     }
	
     public static void main(String args[])
     {
         GenericTest gt1 = new GenericTest();
		
         gt1.input(90);
		
         int i = gt1.output();
		
         System.out.println(i);
     }
 }

Whenever reference gt1 is used in the program, the program behaves according to its type. This results in the Output as shown below:

Output for integer type

90

Similarly at the line,

GenericTest<Double> gt2 = new GenericTest<Double>();

Reference of Double type is passed to class GenericTest , and it takes the place of T at runtime.

Let’s see how it happens, through diagram shown below:

Java Programming Language Generics Example 2

Java compiler removes all the T’s and replaces by Double. Therefore program behaves as if it already contains Double type wherever T is located as shown below:

 public class GenericTest 
 {
     Double var;
	
     public void input(Double var)
     {
         this.var = var;
     }
	
     public Double output()
     {
         return var;
     }
	
     public static void main(String args[])
     {
         
         GenericTest gt2 = new GenericTest();
		
         gt2.input(99.99);
		
         double d = gt2.output();
		
         System.out.println(d);
     }
 }

Whenever reference gt2 is used in the program, the program behaves according to its type. This results in the Output as shown below:

Output for Double type

99.99

Some more advantages of Generics

1) Resolves Type casting problem
2) Provides Type Safety

1) Resolves Type casting problem

Generics resolves type casting problem.
To prove it, let’s try to use Object class reference in the above generic program instead of the Generic type parameter.

 public class NonGenericTest 
 { 
     Object var;
	
     public void input(Object var)
     {
         this.var = var;
     }
	
     public Object output()
     {
         return var;
     }
	
     public static void main(String args[])
     {
         NonGenericTest gt1 = new NonGenericTest();
         NonGenericTest gt2 = new NonGenericTest();
		
         gt1.input(90);
         gt2.input(99.99);
		
         int i = (int) gt1.output(); // type-casting required
         double d = (double) gt2.output(); // type-casting required
		
         System.out.println(i);
         System.out.println(d);
     } 
 }

Output

90
99.99

Now you can see that we are successful in achieving the same output as that of the previous generic program by using Object class reference instead of the Generic type parameter, but it required type-casting at lines,

int i = (int) gt1.output();

                                                                          And

double d = (double) gt2.output();

This is one of the major disadvantages of using Object class reference, whereas Generics concept helps us to avoid the use of type casting.

2) Provides Type Safety

Generics provide type safety.
To prove it, let’s write another program using Object class reference.

 public class NonGenericTest2 
 {
	
     public void input1(Object var)
     {
         int i = (int) var;
         System.out.println(i);
     }
	
     public void input2(Object var)
     {
         int i = (int) var;           // runtime exception occurs
         System.out.println(i);
     }
	
     public static void main(String args[])
     {
         NonGenericTest2 gt1 = new NonGenericTest2();
		
         gt1.input1(999);
         gt1.input2("Hello!");
     }
 }

Output

999
Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer
               at generics_Intro_1.NonGenericTest2.input2(NonGenericTest2.java:14)
               at generics_Intro_1.NonGenericTest2.main(NonGenericTest2.java:23)

This program compiles fine because we have used Object type reference to receive integer & string values.
Since Object class is a superclass of all the classes, the compiler allows Object class reference to refer any integer & string values.
But the real problem arises during runtime at the line,

 int i = (int) var;

inside input2() method because we are trying to assign a string value to int variable i.
Compiler does not have any way to know about this problem at compile time. Therefore compiler does not warn us about this problem at compile time and got a runtime exception.

To overcome this problem, Java provided Generics concept that alerts us at compile time by showing compile time error.

Example

 public class GenericTest2<T>; 
 {

     public void input1(T var)
     {
         int i = (int) var;
         System.out.println(i);
     }
	
     public void input2(T var)
     {
         int i = (int) var;
         System.out.println(i);
     }
	
     public static void main(String args[])
     {
         GenericTest2<Integer> gt1 = new GenericTest2<Integer>();
		
         gt1.input1(999);
         //gt1.input2("Hello!");
     }
 }

Output

999

If we removed the comment at the line,

gt1.input2("Hello!");

We will receive compile time error saying,

The method input2(Integer) in the type GenericTest2<Integer> is not applicable for the arguments (String)

This prevents causing an exception at the runtime.

Therefore now it can be said that Generics provide type safety.

Note

It is not compulsory to use type parameter as T only that is <T>. It can be anything like ABCD, etc.

 

Leave a reply