Generic Class and Constructor

In this article, we will understand what is a Generic class and a Generic constructor in java with proper examples.

Generics are used for type safety, code reuse, and good performance. Suppose you have written a program and it has compiled successfully, now while executing this program you want the program to run without any issues. But there might be a situation where your code throws an exception at runtime because of a type mismatch. This is one of the issues that we can take care of at the compilation level using the generics feature.

package com.generics;

class StoreObject {

  Object object;	

  public Object getObject() {
	return object;
  }

  public void setObject(Object object) {
	this.object = object;
  }	
}

public class ClassCastException {

  public static void main(String[] args) {	

	StoreObject storeObject = new StoreObject();
	storeObject.setObject("Suraj");

	// We have to cast 'Object' to a specific type as it is not type-safe 
		
	String name = (String) storeObject.getObject();
	System.out.println("Stored name is : "+name);
	int result = (int) storeObject.getObject();		
	System.out.println("Stored result is : "+result);	
  }
}

Output: Stored name is : Suraj
Exception in thread “main” java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer at com.generics.ClassCastException.main(ClassCastException.java)

The above code compiles successfully but will throw a runtime exception

The object should be properly typed cast else ClassCastException is thrown

String name = (String) storeObject.getObject();
// Properly cast

int result = (int) storeObject.getObject();
// String cannot be cast to int type hence ClassCastException

Type safety

Generics help us to achieve type safety and also removes the extra burden of casting variables from one form to another.

In the above example, we have declared the variable type as an Object. Now instead of using the Object type, we will make use of the type T.

T refers to any type.

Hence while instantiating the class whatever type we provide in the diamond operator that same type we have to use throughout the program. Let us try to understand it better with an example.

package com.generics;

class StoreGenericType<T> {

  T object;

  public T getObject() {
	return object;
  }

  public void setObject(T object) {
	this.object = object;
  }
}

public class GenericType {

  public static void main(String[] args) {

	StoreGenericType<String> storeGenericType = new StoreGenericType();

	storeGenericType.setObject("Suraj");
	String name = storeGenericType.getObject();
	System.out.println("Stored name is : "+name);
  }
}

O/P : Stored name is : Suraj

Here we use String type while instantiating a class within the diamond operator hence we can use only “String” variables with the “storeGenericType” object. Also, we don’t have to explicitly cast the T variable as the generics feature sorts it out ( Generics is a Compiler feature )

public class GenericType {

  public static void main(String[] args) {	

	StoreGenericType<String> storeGenericType = new StoreGenericType();

	storeGenericType.setObject("Suraj");
	int result = storeGenericType.getObject();
	System.out.println("Stored result is : "+result);
  }
}

Output: Exception in thread “main” java.lang.Error: Unresolved compilation problem: Type mismatch: cannot convert from String to int

Here the program fails at the compilation level, hence the developer gets a chance to correct this program before moving it for execution.

Reusability

We can use the same piece of code with different instances. Check the code below.

package com.generics;

class StoreGenericType<T> {

  T object;

  public T getObject() {
	return object;
  }

  public void setObject(T object) {
	this.object = object;
  }
}

public class GenericType {

  public static void main(String[] args) {

	StoreGenericType<String> storeGenericType = new StoreGenericType();

	storeGenericType.setObject("Suraj");
	String name = storeGenericType.getObject();
	System.out.println("Stored name is : "+name);

        StoreGenericType<Integer> storeGenericType = new StoreGenericType();

	storeGenericType.setObject(10);
	int number = storeGenericType.getObject();
	System.out.println("Stored number is : "+number);
  }
}

O/P : Stored name is : Suraj
      Stored number is : 10

Here the StoreGenericType generic class can use String as well as it can use Integer. We do not have to write separate classes for storing String type and Integer type.

Generic Constructor

We can use generics with a constructor to provide the initialization to class variables.

package com.generics;

class HoldGenericType<T> {

  T object;	

  public HoldGenericType(T object) {
	this.object = object;
  }

  public T getObject() {
	return object;
  }
}

public class GenericsWithConstructor {

  public static void main(String[] args) {	

	HoldGenericType<String> holdGenericType = new HoldGenericType("Suraj");

	String name = holdGenericType.getObject();
	System.out.println("Stored name is : "+name);	
  }
}

O/P : Stored name is : Suraj

Raw Type

At the time of instantiating a Generic class if we do not provide any Type then it is a raw type. If we are making use of Raw type then explicitly we have to do the casting else we will get a compilation issue.

package com.generics;

class HoldGenericType<T> {

  T object;

  public void setObject(T object) {
	this.object = object;
  }

  public T getObject() {
	return object;
  }
}

public class GenericsWithConstructor {

  public static void main(String[] args) {	

	HoldGenericType holdGenericType = new HoldGenericType();
	holdGenericType.setObject("Suraj");	

	String name = (String)holdGenericType.getObject(); // we are explicitly casting it else we will get compilation issue

	System.out.println("Stored name is : "+name);	
  }
}

O/P : Stored name is : Suraj 

Type contradicting scenario

What happens when a generic type provided while instantiating a class is different from a generic constructor parameter?

Let’s try to understand it better with an example :

package com.generics;

class HoldGenericType<T> {

  T object;

  HoldGenericType(T object) {
	this.object = object;
  }

  public T getObject() {
	return object;
  }
}

public class GenericsWithConstructor {

  public static void main(String[] args) {		

	HoldGenericType<String> holdGenericType = new HoldGenericType(10);

	String name =  holdGenericType.getObject();
	System.out.println("Stored name is : "+name);	
  }
}

Output: Exception in thread “main” java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String at com.generics.GenericsWithConstructor.main(GenericsWithConstructor.java)

No compilation exception: The resulting variable or target-type “name” variable is the same as the generic type which we are using while instantiating a class.

Runtime exception: The constructor parameter type we are passing is “int” and the target type is “String”. Integer cannot be cast to String.

These are the basics of Generic classes and Generic constructors. We will cover other topics like “Generic Methods, Bounded Types, Wild Cards, etc” as part of other blogs.

In this article, we have covered what is a Generic class and a Generic constructor in java along with proper examples. I hope you found this article interesting and valuable. Please share this article with your friends and help me grow. If you are having any concerns or questions about this article please comment below. If you want to get in touch with me please visit the Contact Me page and send an email.

Leave a Comment