Generics in Java

A generic class is a special class that generates one or more non-specific java types upon instantiation. This helps remove the runtime exception risk “ClassCastException” when we need to cast between different types. With these generic classes, one has the capability to create classes that work with different java data types. Generics help to improve the quality and efficiency of the code.

What do I mean when I say “remove the risk of runtime exception (ClassCastException)”? alright, let’s get a better understanding by using examples:

Typecasting creating ClassCastException

package generics;

import java.util.ArrayList;
import java.util.List;

public class Generics {
    public static void main(String[] args) 
    {
        List arraylist = new ArrayList();
        arraylist.add("xyz");
        arraylist.add(new Integer(5)); 

    for(Object obj : arraylist){
	
    String s=(String) obj; 
}
    }
    
}

Output:

run:
Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
	at generics.Generics.main(Generics.java:16)
C:\Users\Mozerian\AppData\Local\NetBeans\Cache\8.2\executor-snippets\run.xml:53: Java returned: 1
BUILD FAILED (total time: 1 second)

In the above code we type casted String s=(String) obj which lead to ClassCastException at runtime. This runtime error is caused because the object we are passing in the list is a String where as we declared one of our elements as an Integer type.

No type casting removing ClassCastException

package generics;

import java.util.ArrayList;
import java.util.List;

public class Generics {
    public static void main(String[] args) 
    {
       List<String> arraylist = new ArrayList<String>(); 
        arraylist.add("xyz");
         //compiler error

        for(String s : arraylist){
}

}
    }
   

Output:

run:
BUILD SUCCESSFUL (total time: 0 seconds)

If we remove the String s=(String) obj then we do away with the ClassCastException at runtime. A point to note also if we add list1.add(new Integer(5)) we again get a compile-time error.

Advantages of using Generics

Lets now dig in deeper by looking at the advantages of using generics.

Code reusability

Generics allow code reuse, this means that we can write an interface/class/method and use it for any type

package generics;

import java.util.ArrayList;
import java.util.List;

public class Generics {
    public static void main(String[] args) 
    {
Generics generic = new Generics();
System.out.println("===printing the integer values==");
generic.showData(1,2 );
System.out.println("===printing the string values===");
generic.showData("Generics in", "Java is good");
}
public <T> void showData(T x, T y)
{
System.out.println(x);
System.out.println(y);
}
}

Output:

run:
===printing the integer values==
1
2
===printing the string values===
Generics in
Java is good
BUILD SUCCESSFUL (total time: 1 second)

In the above code, there is nowhere we have declared integer or string, but by using generic, we can reuse the code to print any type we want. We can also print float type just by adding generic.showData(1.212, 2.234);

Compile type safety

Generics help code safety as it allows one to know a code error at compile time rather than at the run time. Using generics, the compiler will show an error at compile time rather than at runtime. How does this help? as a programmer, I believe you know how hard it can be to find an error at runtime, but it is straightforward to find an error at compile time since the editor itself complains.

Removes individual type casting

By use of generics we do not require individual type casting.
Lets take a look at an example:

In the below code, code A requires typecasting since we have not used generics. Once we introduce generics in the code then we do not need to type cast.

package generics;

import java.util.ArrayList;
import java.util.List;

public class Generics {
    public static void main(String[] args) 
    {
   
List arraylist = new ArrayList();
arraylist.add("here we need type casting");
String typecast = (String) arraylist.get(0);


List<String> list = new ArrayList<String>();
list.add("here we do not need type casting");
String notypecast = list.get(0);  

}
}

Last but not list is generics helps in implementing non generic algorithms.

Generic object initialization

A generic object can hold data of one type of more. To initialize object with one type.

Generic <String> gen = new Generic <String>();

To initialize object of two type:

Generic <String,Interger> gen = new Generic <String,Integer>();

This tells the class that the type 1 object is a String and the type 2 object is an Integer. The benefit of this in a generic class is objects of type example with different types given for each so you could initialize another object example with <Double,String>

Types of Generics

Generic class

A generic class is a class that can refer to any data type. Hence you define the type during instantiation.
Generic types are declared using the angle brackets <> around a parameter holder type eg. <E>. We will have a look at the parameters later in this tutorial
Let’s convert a normal java class into a generic class:

Normal class

public class Generics {
    private String name;
    public void set (String setname)
    {
        name = setname;
    }
    public String get()
    {
        return name;
    }

In the above example, we have declared a class, then we went ahead and declared a string variable name that holds string data type. We then used the setter method to set the name to a new name and use the getter method to return it. In the driver class, if we pass a string in the set method and try to cast it as int, then we will receive a casting error at runtime. The string cannot be converted into int.

public static void main(String[] args) 
    {
   Generics cars = new Generics();
   cars.set(T)
   Int number = (int)cars.get();
System.out.println(number);
    }
    }

Supposing we do not want to deal with string data type in this case, we will have to change the data type at declaration. But this can be achieved very easily using a generic class where we can determine the data type we want at instantiation.

Generic class

public class Generics<T> {
    private T t;
    public void set (T setname)
    {
        t = setname;
    }
    public T get()
    {
        return t;
    }
    

Generic driver class

public static void main(String[] args) 
    {
    Generics <Integer> IntValue = new Generics <Integer> ();
    Generics <String> StringValue = new Generics <String>();
    IntValue.set(T);
    StringValue.set("Test");
    int nunmber = IntValue.get();
    String st = StringValue.get();
}

Generic methods

So far, we have created generic classes, but we can also create generic methods outside of a generic class. Just like Type declaration, method declaration is generic. One or more types of parameters parameterize that. Generic methods are methods that can accept any type of argument. It allows both static and non-static methods only that the scope of the arguments is limited to the method where it is declared.

We will use a type interface diamond to create generic methods.

The interface diamond

A type interface diamond enables you to create a generic method as you would an ordinary method, without specifying a type between angle brackets.
But why a diamond?
The angle brackets are often referred to as diamond <>
Typically, if there is only one type inside the diamond, we use <T> where T stands for typeFor two types we would have <K, T>You can use the only non-reversed word as the type holder instead of using <T>. We could have used <T1>. By convection, type parameter names are singles Uppercase letters.

package generics;

import java.util.ArrayList;
import java.util.List;

public class Generics<T> 
{
    public static <K> void printArray(K [] arrayElements){
        
        for(K elements :arrayElements){
            
            System.out.println(elements );  
                      
        }
        
        System.out.println();  
    }
   
    public static void main(String[] args) 
    {
   
    Integer [] arrayInt = {1,2,3,4,5};
    String [] arrayString = {"moses","njorge"};
    Character[] arrayChar = { 'A', 'V', 'C', 'D'};
    
    System.out.println( "Printing Integer Array" );
    printArray( arrayInt  );   


    System.out.println( "Printing String Array" );
    printArray( arrayString  );   
    
    System.out.println( "Printing Char Array" );
    printArray( arrayChar  );   
}
}
  

Output:

run:
===Printing Integer Array===
1
2
3
4
5

==Printing String Array===
Generics
in java is Sweet

===Printing Char Array==
A
B
B
A
BUILD SUCCESSFUL (total time: 1 second)

In the above code we defined a generic method printArray for returning the content of the array. The driver calss in the main method allows multiple array types.

Generic interface

An interface is a java construct that helps define the roles that an object can assume. We can also create a generic interface.

package java.lang;
import java.util.*;

Public interface School <T1,T2>{

  public int School(T1 t);
  public String School(T2 p);
}

An interface is implemented by a class and extended by another interface. Lets implement the interface above.

Public class Faculty implements School<Integer,String>{
Public Integer School (Integer t)
{
  //execution code
}
Public String School (String p)
{
  //execution code
}
}

Generic constructor

A constructor is a special type of method that is used to initialize an object or can be invoked when an object of that class is created. Let’s take a look at an example generic constructor

public class Cars<T> 
{
    private T toyota;
    private T isuzu;
    private T mercedes;
    
    
    public Cars(T toyota, T isuzu, T mercedes)
    {
        super();
        this.toyota = toyota;
        this.isuzu = isuzu;
        this.mercedes = mercedes;
}

In this example the cars class’s constructor has the type information. Therefore you can have an instance of cars with all attributes of a single type only.

Type parameter name

In order to distinguish the generic type parameter names from java variables, the names are made single, uppercase letters. This forms their own name convention. These parameters include.
T-Type
E- element (used extensively by the java collection framework)
N-Number
K-key (Used in Map)
V-Value (Used in Map)
S,U,V, etc- 2nd,3rd,4th types.

Generic WildCards.

In java, we present wildcards using a question mark (?). They do refer to an unknown type. Wildcards with generic allow us to create control of the type we use. We cannot use a wildcard while instantiating a generic class or invoking a generic method.
They fall into two categories

Bounded

The bound types are used when we want to restrict the variable types in a method. They are of two types.

. <? extends type>

To declare this type of bound, you start by listing the type parameter name, followed by extend keyword and lastly upper bound.

public static <T extends Comp<T>> int compa(T t1, T t2){
		return t1.compareTo(t2);
	}

. <?super types>

Let’s take an example we want to add Characters/Integers to a list of Characters/Integers in a method, The super Keyword is used together with a lower bound.

public static void IntegersAdd(List<? super Integer> list){
		list.add(new Integer(50));
	}

Unbounded

<?>denotes an unbounded wildcard
We use unbound type when we want the generic method to work with all data types,
Example: arrayList <?>(rep. arrayList of unknown type)

public static void print(List<?> list){
		for(Object data : list){
			System.out.print(data + "::");
		}
	}

Points to note when working with Generic types

While working with generic types remember the following:

  • The types must be identified at the instantiation of the class
  • Your class should contain methods that set the types inside the class to the type passed into the class upon creating an object of the class
  • One way to look at the generic class is by understanding  what is happening behind the code.

Don’ts in Java Generics

Don’t create static fields of type this will generate a compile time error.

public class Generics<T>
{
   private static T name; 
}

Don’t create instances of T. This will lead to an error.

public class Generics<T>
{
   public Generics(){
      new T();
   }
}

Don’t create a generic exception class. This causes compiler error.

public class Generic<T> extends Exception {}

Don’t create generics with primitives declaration.

final List<int> AddList = new ArrayList<>();

Conclusion

In this tutorial, we have discussed generics in java; we have covered generics class, interface, constructor, and methods. We went further and looked at the generic wildcards and parameters and lastly at the don’ts in generics. With that discussed, it’s evident that generic has become a good feature that all programmers should appreciate since the programmers’ life with the use of generics is made easier.

Similar Posts

Leave a Reply

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