X

Play AlgoQuiz

Rate Us :



Share with Friends :

Menu

Generics :

Arrays are always safe with respect to type. For example : if our program requirement is to add only String objects then we can use String[ ] array. For this array we can add only String type of objects but if by mistake we are trying to add any other type then we will get compile time error.

Example :

String s[ ] = new String[10];
s[0] = "Java"; //valid
s[1] = "Valley"; //valid
s[2] = new Algo(); /* Compile time error : incompatible types
                     found : Algo
                     required : String */


Hence in the case of Arrays we can always give guarantee about the type of elements. String[ ] array contains only String object due to this Arrays are always safe to use with respect to type but Collections are not safe to use with respect to type. For Example, if our program requirement is to hold only String objects and if we are using ArrayList but if by mistake we are trying to add any other type to the List then we won't get any compile time error but program may fail at run time.

Example :

ArrayList l = new ArrayList();
l.add("Algo");
l.add("Java");
l.add(new Valley());

String s1 = (String) l.get(0); //valid
String s2 = (String) l.get(1); //valid
String s3 = (String) l.get(2); // Runtime Exception : ClassCastException

There is no guarantee that Collection can hold a particular type of object. Hence, with respect to type, Collections are not safe to use.

In the case of Arrays at the time of retrieval, it is not required to perform any typecasting.

Example :

String[ ] s = new String[10];
s[0] = "Algo";
String s1 = s[0]; //Typecasting is not required.

But in the case of Collection at the time of retrieval, it is compulsory that we should perform typecasting otherwise we will get compile time error.

Example :

ArrayList l = new ArrayList();
l.add("Algo");
String s1 = l.get(0); /* Compile time error : incompatible types
                                              found : Object
                                              required : String */

ArrayList l = new ArrayList(); l.add("Algo"); String s1 = (String) l.get(0); // valid

Hence, in the case of Collection typecasting is mandatory which is a problem for programmers. To overcome these problems of Collections (i.e. type safe and type casting) Sun Microsystem introduced Generics concept in 1.5 version of Java. Hence, the main objective of Generic concepts are :

  • To provide type safety to the Collections so that they can hold only one particular type of object.

  • To resolve typecasting problems.

To hold only String type of object a Generic version of ArrayList can be declare as follows :

ArrayList <String> l = new ArrayList <String>();

For this ArrayList we can add only String type of objects but if by mistake we are trying to add any other type then we will get compile time error i.e. we are getting type safety.

Example :

ArrayList <String> l = new ArrayList <String>();
l.add("Algo");
l.add("10");
l.add(10); /* Compile time error : cannot find symbol
                                   symbol : method add(int)
                                   location : class ArrayList<String> */

At the time of retrieval it is not required to perform any typecasting.

Example :

ArrayList <String> l = new ArrayList <String>();
l.add("Algo");
String s = l.get(0); //Typecasting is not required
Note :

Usage of parent class reference to hold child class object is considered as polymorphism. Polymorphism concept is applicable only for the base type but not for parameter type.

Example :

ArrayList <Integer> l = new ArrayList <Integer>(); //valid
List <Integer> ls = new ArrayList <Integer>(); //valid
Collection <Integer> c = new ArrayList <Integer>(); //valid
List <Object> ls = new ArrayList <Integer>(); /* Compile time error : incompatible types
                                                                   found : ArrayList <Integer>
                                                                   required : List <Object> */

For the parameter type we can use any class or interface name and we can't use primitive type otherwise we will get compile time error.

Example :

ArrayList <int> l = new ArrayList <int>(); /* Compile time error : unexpected type
                                                                   found : int
                                                                   required : reference */

Generic classes :

Until 1.4 version of Java a non-Generic version of ArrayList class is declared as follows :-

class ArrayList
{
  add(Object o);
  Object get(int index);
}

The argument to the add() method is object. Hence, we can add any type of object due to which we are not getting type safety. The return type of get() method is object so, at the time of retrieval it is compulsory to perform typecasting. But in version 1.5 of Java, Generic version of ArrayList class is declared as follows :-

class ArrayList<T>
{
  add(T t);
  T get(int index);
}

Based on our runtime requirement Type parameter 'T' will be replaced with corresponding provided Type.

To hold only String type of object we have to create Generic version of ArrayList object as follows:-

ArrayList <String> l = new ArrayList <String>();

For this requirement the corresponding loaded version of ArrayList class is :

class ArrayList<String>
{
  add(String s);
  String get(int index);
}

add() method can take String as the argument hence, we can add only String type of objects but by mistake if we are trying to add any other type then we will get compile time error i.e. we are getting type safety. The return type of get() method is String so, at the time of retrieval we can assign directly the String type variable and it is not required to perform any typecasting.

Note :

As the type parameter we can use any valid Java identifier but it is convention to use 'T'.

We can pass any number of Type parameter and need not to be of one class.

Example :

HashMap <String , Integer> m = new HashMap <String , Integer>();

Through Generic we are associating a Type parameter to the classes. Such type of parameterized classes are called Generic classes. We can define our own Generic classes also.

Example :

class Gen<T> { T obj; Gen(T obj) { this.obj = obj; } public void show() { System.out.println("The object of type " + obj.getClass()); } public T getObj() { return obj; } } class Example { public static void main(String[ ] args) throws Exception { Gen <String> s = new Gen <String>("Algo"); s.show(); System.out.println(s.getObj()); } } //save as : Example.java //compile as : javac Example.java //run as : java Example

Output :

The object of type class java.lang.String Algo

Bound types :

We can bound the Type parameter for a particular range by using extends keyword.

class Algo<T>
{

}

As a Type parameter we can pass any type hence it is an unbounded type :

Algo <Integer> i = new Algo <Integer>(); // valid
Algo <String> s = new Algo <String>(); //valid

class Algo<T extends Number>
{

}

As the Type parameter we can pass either Number type or its child classes hence, it is a bounded type :

Algo <Integer> i = new Algo <Integer>(); //valid
Algo <String> s = new Algo <String>(); /* Compile time error : Type parameter java.lang.String 
                                                               is not within its bound */

We can't bound Type parameter by using implements and super keyword. But implements keyword purpose will be achieved by using extends keyword only.

class Algo<T extends X>
{

}
Note :

  • 'X' can be either class or interface.

  • If 'X' is a class then as the Type parameter we can provide either 'X' type or it's child classes.

  • If 'X' is an interface as the Type parameter we can provide either 'X' type or it's implementation classes.

class Algo<T extends Runnable>
{

}

Algo <Runnable> r = new Algo <Runnable>(); //valid
Algo <Thread> t = new Algo <Thread>(); //valid
Algo <String> s = new Algo <String>(); /* Compile time error : Type parameter java.lang.String 
                                                               is not within its bound */

We can bound the Type parameter even in combination also.

class Algo<T extends Number & Runnable>

As the Type parameter we can pass any type which is the child of class Number and implements Runnable interface.

class Algo<T extends Runnable & Comparable> //valid
class Algo<T extends Number & Runnable & Comparable> //valid

class Algo<T extends Number & Thread> 
//invalid : we can't extend more than one class at a time

class Algo<T extends Runnable & Number> 
//invalid : we have to take first class and then interface

Generic methods and Wild card character :


m1(ArrayList <String> l) :-

This method is applicable for ArrayList <String> (ArrayList of only String type). Within this method we can add String type object and null to a List but if we are trying to add any other type then we will get compile time error.

Example :

m1(ArrayList <String> l)
{
  l.add("Algo"); //valid
  l.add(null); //valid
  l.add(10); //invalid
}

m1(ArrayList <?> l) :-

We can call this method by passing ArrayList of any type but within the method we can't add any type accept null to the list because we don't know the exact type.

Example :

m1(ArrayList <?> l)
{
  l.add("Algo"); //invalid
  l.add(null); //valid
  l.add(10); //invalid
}

m1(ArrayList <? extends X> l) :-

If 'X' is the class then we can call this method by passing ArrayList of either 'X' type or it's child classes. If 'X' is an interface then we can call this method by passing ArrayList of either 'X' type or it's implementation class. In this case also we can't add any type of elements to the list except null.


m1(ArrayList <? super X> l) :-

If 'X' is a class then this method is applicable for ArrayList of either 'X' type or it's super classes. If 'X' is an interface then this method is applicable for ArrayList of either 'X' type or super classes of implementation class of 'X'. Within this method we can add only 'X' type objects and null to the list.

Example :

ArrayList <String> l = new ArrayList <String>(); //valid
ArrayList <?> l = new ArrayList <String>(); //valid
ArrayList <? extends String> l = new ArrayList <String>(); //valid
ArrayList <? super String> l = new ArrayList <String>(); //valid
ArrayList <? extends Object> l = new ArrayList <String>(); //valid
ArrayList <? extends Number> l = new ArrayList <Integer>(); //valid

ArrayList <? extends Number> l = new ArrayList <String>(); 
/* Compile time error : Incompatible types
found : ArrayList <String>
required : ArrayList <? extends Number> */

ArrayList <?> l = new ArrayList <? extends Number>(); 
/* Compile time error : Incompatible types
found : ArrayList <? extends Number>
required : ArrayList <?> */

ArrayList <?> l = new ArrayList <?>(); 
/* Compile time error : Unexpected types
found : ?
required : class or interface without bounds */

We can define the Type parameter either at class-level or at method-level.

Declaring Type parameter at class-level :


class Algo<T>
{
  T obj;
  public T getobj()
  {
    return obj;
  }
}

Declaring Type parameter at method-level :

We have to declare the Type parameter just before return type.

class Algo
{
  public <T> void m1(T t)
  {
    T obj;
  }
}

Communication with non-Generic code :

To provide compatibility with old version Sun Microsystem compromised the concept of Generics in very few areas.

Example :

import java.util.*; class Example { public static void main(String[ ] args) throws Exception { ArrayList <String> l = new ArrayList <String>(); l.add("Algo"); //l.add(10); m1(l); System.out.println(l); //l.add(10); } //Generic area public static void m1(ArrayList l) { l.add(10); l.add(10.5); l.add(true); } //non-Generic area } //save as : Example.java //compile as : javac Example.java //run as : java Example

Output :

[Algo, 10, 10.5, true]

Note :

Generic concept is applicable only at compile time to provide type safety and to resolve typecasting problems. At runtime there is no such type of concept. Hence, the following declaration are equal :-

ArrayList l = new ArrayList(); 
ArrayList l = new ArrayList <String>(); 
ArrayList l = new ArrayList <Integer>(); 
 

Example :

import java.util.*; class Example { public static void main(String[ ] args) throws Exception { ArrayList l = new ArrayList <String>(); l.add("Algo"); l.add(10); l.add(true); System.out.println(l); } } //save as : Example.java //compile as : javac Example.java //run as : java Example

Output :

[Algo, 10, true]

The following two declarations are equal and there is no difference :-

ArrayList <String> l = new ArrayList <String>();
ArrayList <String> l = new ArrayList();


X

MiniDoll :

MiniDoll is an application to learn with fun. You can play it to solve some tricky questions related to Java Programming. Either enjoy playing it alone or discuss with your friends to solve it. It is a simple approach to learn by solving questions. This application has different topics of Java and each topic is divided into three phases. Start solving from initial topic to final topic and by the end of every topic you can boost your concept of that topic.