Architecture & DesignThe Key Features of Java Generics

The Key Features of Java Generics

Developer.com content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More.

Java generics was introduced (as a new feature in J2SE 5) to leverage the general model of programming. Generics improves code clarity and compactness, and adheres to one of the basic principle of Software Engineering—code reusability. To put it simply, imagine a sorting function that sorts four distinct arrays such as—an integer array, double array, character array, and string array. Instead of writing a bunch of overloaded functions, we can achieve the same result with the help of a single generic method. Similarly, say, with a single generic class we can implement the Stack data structure and have a polymorphous utility with variety of data types. This is an exceptional capability and can be quite helpful in situations where we generally use boilerplate code to logically mean the same thing. Java generics provides the necessary syntactic element to make a generic interface to our polymorphic needs. The article takes up some of the key aspects of Java generics with relevant examples to delineate the idea. Refer to “Using Java Generics to Leverage Refactoring” for more basic information on the topic.

Java Generics and the Object Class

The oddity of Java generics is in its syntax that, upon inception, led to revamp the Java core API structure, especially in retrospect to housekeep backward compatibility (version prior to J2SE 5). Generics can be applied to create generic methods as well as classes and interfaces. The Java Language Specification specifies generics as parameterized types; this means the types that enable us to create classes, interfaces, methods, and their operable data are specified as parameters. If we emphasize that generics is the only means to create a sense of a general method of coding in Java, our argument simply falls short of a ratiocinate judgment. The reason is, we can easily create classes and methods that behave as generics with the help of an Object class reference type. Object is the default or implicit superclass of any and every class in Java. So, by virtue of the basic object-oriented principle, it can reference any subtype. But, there is a significant difference with respect to generics. It added the element of type safety that was lacking with the Object classes. Due to this functionality, the explicit typecasting that was required in the process of translating Object and the type of operable data is absent—it became implicit and automatic.

public static <E> void display(E[] arr) {
   for (E e : arr)
      System.out.printf("%s ", e);
   System.out.println(arr.getClass().getName());
}

Observe that the generic display method also be very can easily written with the help of the Object class, as follows.

public static void display2(Object[] obarr) {
   for (Object o : obarr)
      System.out.printf("%s ", o);
   System.out.println(obarr.getClass().getName());
}

This seems harmless and we can easily interchange generics with the Object reference. Unfortunately, there is a severe problem of type safety that we have simply ignored. The code here is so simple that it doesn’t need any type safety, but the problem still remains.

Type Safety

Let’s create two classes, one a generic implementation and another generalised with the help of the Object reference. This specific problem will illustrate the idea of compile time type safety ensured by generics but not by an Object reference.

package org.mano.example;

public class Data<E> {
   private E value;

   public Data(E val){
      value=val;
   }

   public E getValue(){
      return value;
   }

   public String getObjectType(){
      return value.getClass().getName();
   }


   public static void main(String[] args){
      Data<Integer> intData=new Data<>(20);
      System.out.println(intData.getObjectType()+
         " value is: "+intData.getValue());

      Data<Double> doubleData=new Data<>(20.89);
      System.out.println(doubleData.getObjectType()+
         " value is: "+doubleData.getValue());

      Data<String> strData=new Data<>("Generics is fun!");
      System.out.println(strData.getObjectType()+
         " value is: "+strData.getValue());

      intData=doubleData;   // ...error

   }
}

Here, intData=doubleData; is not allowed; the error is caught at compile time. On the other hand, in the following example:

package org.mano.example;

public class Data2 {

   private Object value;

   public Data2(Object val){
      value=val;
   }

   public Object getValue(){
      return value;
   }

   public String getObjectType(){
      return value.getClass().getName();
   }


   public static void main(String[] args){
      Data2 intData=new Data2(20);
      System.out.println(intData.getObjectType()+
         " value is: "+intData.getValue());

      Data2 doubleData=new Data2(20.89);
      System.out.println(doubleData.getObjectType()+
         " value is: "+doubleData.getValue());

      Data2 strData=new Data2("Generics is fun!");
      System.out.println(strData.getObjectType()+
         " value is: "+strData.getValue());

      intData=doubleData;
   }
}

with the statement intData=doubleData; the compiler has no idea of the error until runtime. This simply shows there is no type checking with Object reference. When coding, it is very difficult to pinpoint such an error and can create lot of trouble if goes unnoticed.

Restrictions in Generics

A generic type cannot be instantiated directly because the instantiation compiler must know the data type to allocate memory. So, the following code is erroneous.

public class MyClass<E extends Number> {
   private E obj;

   public MyClass(E x, E[] elements){
      obj=new E(); //...error

   }
}

One cannot declare static members as follows:

public class MyClass<E extends Number> {
   static E obj; //...error
}

Instantiating in the following manner also do not make sense because the compiler needs to know the size of the data type before having consecutive memory allocation of the type.

public class MyClass<E extends Number> {
   E []arr=new E[10]; //... error

}

It is not possible to create a Generic Exception class.

Conclusion

These are some of the rudimentary ideas to get started with Generics in Java. Generics are particularly useful in generalizing repetitive tasks into a single block. This leverages the reusability, clarity, and compactness of the code. The Collection framework APIs extensively use Generics. Another area where Generics really proves helpful is in database programming because there is a lot of boilerplate code involved while implementing a CRUD application. These, when generalised, makes debugging and testing easy.

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Latest Posts

Related Stories