Generic Feature in Java


Java is getting bigger and popular now. I think the OpenSource is one of the reason that influence it. People always look for something that are free and powerful. One of my friends who’s currently working as PHP developer told me that he’s trying to switch to Java. He came to me yesterday and has asked me about Generic.
In order to answer his question, i’m gonna write some explanation here about Generic. Hopefully this could help him and the others (including me :-)).

What is Generic ?

Generic is one of the java’s feature/extension that starting from Java 5 (Tiger). In my opinion, Generic helps your code more stable by trying to reduce as many as possible any exception that could be raised in runtime. This could be done by trusting the compiler to check our code in the compile time. Generic helps you to detect your program bugs at compile time as many as possible. So, it’s guarantee that our running code is less of any error.
Generic allows you to abstract over types. The most common examples are container types, such as those in the Collection hierarchy. Overall, Generics are heavily uses by these classes (i.e. Collection classes).

Generic vs Non-Generic

The example on this section will give you some short explanation about the differences between using non-Generic class and Generic class. In this first example, I won’t use Collection classes here, but I’ll use just a simple ordinary class to shows you about how this Generic working.
Let say, I have two containers. Each container could be filled with 2 objects, whether Car or Motorcycle.
So, first I’ll create here two prototype for those 2 objects, I’m sure you’ve already know what to do here :-). Exactly, first we are going to create 2 simple POJO classes here called Car and Motorcycle as you can see below:
Car.java

public class Car {

   private long id;
   private String merk;
   private int year;
   private String description;
   private Color color; 
   
   public Car() {}

   public long getId() {
      return id;
   }
  
   public void setId(long id) {
      this.id = id;
   }

   public String getMerk() {
      return merk;
   }

   public void setMerk(String merk) {
      this.merk = merk;
   }

   public int getYear() {
      return year;
   }

   public void setYear(int year) {
      this.year = year;
   }

   public String getDescription() {
      return description;
   }

   public void setDescription(String description) {
      this.description = description;
   }

   public Color getColor() {
      return color;
   }

   public void setColor(Color color) {
      this.color = color;
   }

   @Override
   public String toString() {
      return merk + " " + (description != null ? description : "");
   }
}

Motorcycle.java

public class Motorcycle {

   private long id;
   private String vendor;
   private String merk;
   private int year;
   private Color color;
   private byte gear;
   private boolean monoshock;

   public Motorcycle() {}

   public long getId() {
      return id;
   }

   public void setId(long id) {
      this.id = id;
   }

   public String getVendor() {
      return vendor;
   }

   public void setVendor(String vendor) {
      this.vendor = vendor;
   }

   public String getMerk() {
      return merk;
   }

   public void setMerk(String merk) {
      this.merk = merk;
   }

   public int getYear() {
      return year;
   }

   public void setYear(int year) {
      this.year = year;
   }

   public Color getColor() {
      return color;
   }

   public void setColor(Color color) {
      this.color = color;
   }

   public int getGear() {
      return gear;
   }

   public void setGear(byte gear) {
      this.gear = gear;
   }

   public boolean isMonoshock() {
      return monoshock;
   }

   public void setMonoshock(boolean monoshock) {
      this.monoshock = monoshock;
   }

   @Override
   public String toString() {
      return vendor + " " + merk;
   }
}

and, here is the Enum type for Color attribute:
Color.java

public enum Color {
   RED, BLACK, WHITE, BLUE, MAROON, YELLOW, GREEN, SILVER
}

Next, our “Container” class as you can see below:
Container.java

public class Container {
   private Object object;

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

   public Object getContent() {
      return object;
   }
}

You can see that since our container could be filled with Car or Motorcycle, here in Container class I use Object as it’s type because you know, every class you’ve defined in Java automatically is a subclass of Object class. So, you’re free to pass in whatever you want to the container, whether Car or Motorcycle.
To see how all of these stuffs working, create the tester class as follows:
ContainerTester

public class ContainerTester {

   public static void main(String[] args) {
      Container container = new Container();
      
      Car car = new Car();
      car.setId(1);
      car.setMerk("Ford Escape");
      car.setYear(2009);
      car.setColor(Color.BLACK);
      container.addContent(car);
      System.out.println((Car) container.getContent());
      
      Container container2 = new Container();
      Motorcycle motorcycle = new Motorcycle();
      motorcycle.setId(1);
      motorcycle.setVendor("YAMAHA");
      motorcycle.setMerk("VIXION");
      motorcycle.setYear(2011);
      motorcycle.setColor(Color.BLUE);
      motorcycle.setGear((byte) 6);
      motorcycle.setMonoshock(true);
      container2.addContent(motorcycle);
      System.out.println((Motorcycle) container2.getContent());
   }
}

Compile ContainerTester.java, then running it (see the picture below):
Generic

In the ContainerTester class, we create object Car and Motorcycle and pass them to our Container object via addContent method. Then get them with getContent method, and print to the standard output (i.e. console).
But you can see there, that we have to explicitly casting from Object type to the appropriate type (i.e. Car or Motorcycle) before printing it to the standard output. We know that the cast from Object to appropriate type is correct because we’ve honored the “contract”, but notice that actually the compiler knows nothing about this. It will do nothing to prevent a programmer from passing in an object of the wrong type, such as String.
See the code below:
ContainerTester2.java

public class ContainerTester2 {

   public static void main(String[] args) {
      Container container = new Container();

      container.addContent("FORD RANGER");
      System.out.println((Car) container.getContent());
   }
}

the above code will be compiled successfully, but you’ll get an exception in the runtime when you running the code as you can see from the picture below.

That is clearly a bug, but because the code still compiles, you wouldn’t know anything is wrong until runtime when the application crashes with a ClassCastException.
Here, Generic will help to stabilize your code by detecting as many as possible bugs in the compile time.
So, in the next step I’ll design the Container class with Generic, so this kind of mistake (ClassCastException) would have been caught by the compiler, instead of crashing the application at runtime.
Lets update our Container class to use Generic as you can see below:
Container.java

public class Container<T> {
   private T t;

   public void addContent(T t) {
      this.t = t;
   }
 
   public T getContent() {
      return t;
   }
}

You can see that I’ve replaced all occurences of Object with T which is known as formal type parameter of the Container class. T is a special kind of variable whose value will be whatever type you pass in (any class type, interface type, or even another type variable except primitives data type).
Actually, you could use any other letters (instead of T) for type parameters. Just keeping in your mind that conventionally, type parameter names are single uppercase letter.
The most commonly used type parameter names are:

  • E – Element (used extensively by the Java Collection Framework)
  • K – Key
  • N – Number
  • T – Type
  • V – Value
  • S,U,V, etc for 2nd,3th,and 4th types

Notice that if you’re using multiple type parameters, each parameter must be unique within its declaring class or interface. For example, a declaration of public class Container<T,T> will generate an error on the second occurence of T. So, you can use something like public class Container<T,U> as long as they have a unique letter.
Until here, we’ve designed our new Container generic class. Next, in order to use this generic class, we have to perform a generic type invocation. This could be done by replaces our type parameter (T or U or etc) with our concrete type (known as type argument).
Actually generic type invocation is similiar with an ordinary method invocation. What make difference is: in the ordinary method invocation we pass in arguments as its parameters, but in the generic type invocation we pass in type arguments for its parameters.
An invocation of generic type also known as parameterized type.
So, I’m gonna change my ContainerTester.java to perform “generic type invocation” as you can see below:
ContainerTester.java

public class ContainerTester {
   public static void main(String[] args) {
      Container&lt;Car&gt; container = new Container&lt;Car&gt;(); // parameterized type (assigned to Car type)      
      Car car = new Car();
      car.setId(1);
      car.setMerk(&quot;Ford Escape&quot;);
      car.setYear(2009);
      car.setColor(Color.BLACK);
      container.addContent(car);
      System.out.println(container.getContent()); // no explicit casting anymore
      
      Container&lt;Motorcycle&gt; container2 = new Container&lt;Motorcycle&gt;(); // parameterized type (assigned to Motorcycle)
      Motorcycle motorcycle = new Motorcycle();
      motorcycle.setId(1);
      motorcycle.setVendor(&quot;YAMAHA&quot;);
      motorcycle.setMerk(&quot;VIXION&quot;);
      motorcycle.setYear(2011);
      motorcycle.setColor(Color.BLUE);
      motorcycle.setGear((byte) 6);
      motorcycle.setMonoshock(true);
      container2.addContent(motorcycle);
      System.out.println(container2.getContent()); // no explicit casting type anymore     

      System.exit(0);
   }
}

We can see that we don’t have to provide a cast anymore, because we’ve already designed our Container class with Generic.
Now, with our new Container generic class, if we’re trying to compile our previous ContainerTester2 class, the compiler will give us a warning such as follows:

Generic Method and Generic Constructor

In our previous section, I’ve showed you about the implementation of Generic in Class. Actually, Generic not only could be applied in Class or Interface, but also could be applied in method and constructor.
In this section, I’ll show you about how to apply Generic in method and constructor.
Using our previous example, I’ll redesign the Container class by adding one static method named arrangeContent that give our Container class the ability to fill the container with many objects using Java Collection API.
Container.java

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

public class Container&lt;T&gt; {
   private T t;

   public void addContent(T t) {
      this.t = t;
   }
 
   public T getContent() {
      return t;
   }

   public static synchronized &lt;U&gt; List&lt;U&gt; arrangeContent(U u, List&lt;U&gt; containers) {
      if (containers == null) {
         containers = new ArrayList&lt;U&gt;();
      }
      if (u != null) {
         containers.add(u);
      }
      return containers;
   }
}

You can see that I’ve added one static method arrangeContent. arrangeContent method is generic method, as you can see I’ve included one type parameter U. Notice that you can choose whatever letters you like as long as it has unique name. I also make the arrangeContent thread-safe, and I assume you’ve already knew why do I make that method thread-safe :-).
The next step is to change our ContainerTester.java as follows:
ContainerTester.java

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

public class ContainerTester {
      Container&lt;Car&gt; container = new Container&lt;Car&gt;(); // parameterized type (assigned to Car type)      
      List&lt;Car&gt; containers = new ArrayList&lt;Car&gt;();

      Car car = new Car();
      car.setId(1);
      car.setMerk(&quot;Ford Escape&quot;);
      car.setYear(2009);
      car.setColor(Color.BLACK);
      //container.addContent(car);
      //System.out.println(container.getContent()); // no explicit casting anymore
      containers = Container.&lt;Car&gt;arrangeContent(car, containers); //generic method called
      
      car = new Car();
      car.setId(2);
      car.setMerk(&quot;HONDA New CRV&quot;);
      car.setYear(2010);
      car.setColor(Color.SILVER);
      containers = Container.arrangeContent(car, containers); //generic method called using type inference

      for (Car c : containers) {
         System.out.println(c);
      }
      
      Container&lt;Motorcycle&gt; container2 = new Container&lt;Motorcycle&gt;(); // parameterized type (assigned to Motorcycle)
      List&lt;Motorcycle&gt; containers2 = new ArrayList&lt;Motorcycle&gt;();

      Motorcycle motorcycle = new Motorcycle();
      motorcycle.setId(1);
      motorcycle.setVendor(&quot;YAMAHA&quot;);
      motorcycle.setMerk(&quot;VIXION&quot;);
      motorcycle.setYear(2011);
      motorcycle.setColor(Color.BLUE);
      motorcycle.setGear((byte) 6);
      motorcycle.setMonoshock(true);
      //container2.addContent(motorcycle);
      //System.out.println(container2.getContent()); // no explicit casting type anymore     

      containers2 = Container.&lt;Motorcycle&gt;arrangeContent(motorcycle, containers2); //generic method called
      
      motorcycle = new Motorcycle();
      motorcycle.setId(2);
      motorcycle.setVendor(&quot;KAWASAKI&quot;);
      motorcycle.setMerk(&quot;NINJA RR&quot;);
      motorcycle.setYear(2010);
      motorcycle.setColor(Color.GREEN);
      motorcycle.setGear((byte) 6);
      motorcycle.setMonoshock(true);
      containers2 = Container.arrangeContent(motorcycle, containers2); //generic method called using type inference

      for (Motorcycle motor : containers2) {
         System.out.println(motor);
      }

      System.exit(0);
   }
}

You can see from the code above, to invoke generic method we have two styles there.

  • first is by specifying a type between angle brackets (I called this explicit call).
    e.g.: Container.arrangeContent(car, containers);
  • the second way is called type inference which allows you to invoke a generic method as you would an ordinary method, without specifying a type between angle brackets (i.e. omit the type parameters).
    e.g.: Container.arrangeContent(car, containers);

Generic constructor is quite similiar with generic method.
In order to declare generic constructor, you could do the following:
Container.java

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

public class Container&lt;T&gt; {
   private T t;

   /**
    * Default Container's Generic Constructor
    */
   &lt;T&gt; Container() {
   }

   /**
    * Predefined Container's Generic Constructor
    * @param T 
    */
   &lt;T&gt; Container(T t) {
      this.t = t;
   }

   public void addContent(T t) {
      this.t = t;
   }
 
   public T getContent() {
      return t;
   }

   public static synchronized &lt;U&gt; List&lt;U&gt; arrangeContent(U u, List&lt;U&gt; containers) {
      if (containers == null) {
         containers = new ArrayList&lt;U&gt;();
      }
      if (u != null) {
         containers.add(u);
      }
      return containers;
   }
}

Bounded Type Parameters

Bounded type parameters is usefull when you want to restrict the kinds of types that are allowed to be passed as type parameter.
I’ll show you here by using our earlier examples.
First, I will create new class (supertype class) named Vehicle as follows:
Vehicle.java

public class Vehicle {
}

then, modify Car and Motorcycle classes to extend Vehicle.

public class Car extends Vehicle {
.....
}


public class Motorcyel extends Vehicle {
.....
}

finally, modify our Container class to use bounded type parameter by list the type parameter’s name followed by the extends keyword and it’s upperbound as you can see below:

public class Container<T extends Vehicle> {
.....
}

Now, if you are trying to compile the ContainerTester2.java, you will get an error as you can see from the picture below:

That happens because we are trying to provide String as type argument for Container class, but we’ve already restricted our Container class with bounded type parameter (i.e. Vehicle).
You could also specify additional interfaces that must be implemented by using & (ampersand) character like the following:
<T extends Vehicle & CustomInterface>

Wildcards Type Parameter

Notice that you can also use wildcard for type parameter.
In generic, we use wildcard to represent an unknown type by using character “?”.
for example:

public class Container<? extends Vehicle> {
.....
}

could be read as “some kind of Vehicle”. We call this a bounded wildcard, where Vehicle known as the upper bound.
You can also specify a lower bound by using super keyword like the following:

public class Container<? super Car> {
.....
}

coud be read as “an unknown type that is supertype of Car, possibly Car itself”.
OK, that’s all about generic from me. Before I end this article, there are some terms about generic that maybe you need to know as the following:

  • Type Erasure: is a process where compiler removes all information related with type parameters and arguments in order to keep compatibility with Java legacy code.
  • Raw Type: is a generic class or interface name without any type arguments. This is strongly related with type erasure. For example, if you compile the generic class Container<Vehicle>, then the compiler will remove the type parameter, so your generic class will be translated to Container (without any type arguments). This is the raw type
  • non-reifiable type: is a type that is not completely available at runtime. Container, ArrayList are examples of non-reifiable types, because the compiler going to remove all type arguments (type erasure), so the generic types don’t exist at runtime.
  • Heap Pollution: is a condition that occurs when a variable of parameterized type refers to an object that is not of that parameterized type. This situation occurs when a program performed some operation that would give rise to an unchecked warning at compile time. For example, look at the following code:
    HeapPollutionDemo.java

    import java.util.ArrayList;
    import java.util.List;
    
    public class HeapPollutionDemo {
       
       public static void main(String... args) {
          List motors = new ArrayList<Motorcycle>();
          List<Vehicle> vehicles = motors; //generates an unchecked warning at compile time
          motors.add(0, new Motorcycle()); // generates an unchecked warning at compile time
    

    the result:

About these ads

One thought on “Generic Feature in Java

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s