Java generics

Generics:

Why do We Need Generics In Java?

·    Errors are integral part of coding. Some errors occur at compile time and some errors occur at run time. Errors which occur at compile time can be easily identified and can be removed. But, run time errors occur when an application is running in real time. If they happen, they cause abrupt termination of an application.

·    ClassCastException is also such an exception which happens only at run time. It occurs when data of one type can not be casted to another type. You will never get a single clue about this exception during compilation. Look at the below code which throws ClassCastException at run time. But, you will never be get notified about this exception at compile time.

public class GenericsInJava{    public static void main(String[] args)    {        ArrayList list = new ArrayList();
        list.add(“JAVA”);
        list.add(123);
        for (Object object : list)        {            //Below statement throws ClassCastException at run time
            String str = (String) object;       //Type casting
            System.out.println(str);        }    }}

In this example, ‘list’ contains elements of String type as well as int type. When you try to cast it’s elements to string type in the for loop, element of string type is casted without throwing errors but element of int type throws ClassCastException.

You can avoid ClassCastException by using generics in your code. The above example can be re-written using generics like below.

{    public static void main(String[] args)    {        ArrayList<String> list = new ArrayList<String>();
        list.add(“JAVA”);
    //    list.add(123);       Compile time error
        for (String str : list)        {            //No type casting needed. ClasscastException Never occurs
            System.out.println(str);        }    }}

·    Now, ‘list’ is declared so that it can hold only string type. If you try to add elements of different type, it gives compile time error. Therefore, ClassCastException never occurs while executing the for loop.

·   Generics are introduced in Java 5 to provide the type checking at compile time. If you use

      generics, you need not to perform the type casting explicitly. Java compiler applies strong

      type checking if you use generics in your code and shows errors if the code violates the 

      type safety. Thus removing the risk of ClassCastException.

·   Therefore, To write the type safety code and to remove the risk of ClassCastException at run time, we need generics.

 Defining Generic Class:

Generics are used to check the type compatibility at the compile time and hence removing the chances of occurring ClassCastException at run time. In this particular post, we will see how to define our own generic class.

Generic Class :

 The syntax for defining generic class is as follows,

public class Class_Name<T1,T2,…….Tn>{//Generic Type or Parameterized type}

Where T1, T2, T3 … Tn (T stands for Type) enclosed within angle brackets (<>) are called type parameters and class ‘Class_Name‘ is called generic type or parameterized type.

Now, let’s try to define one generic class based on the above format.

class GenericClass<T>{    T t;
    public GenericClass(T t)    {        this.t = t;    }
    public void setT(T t)    {        this.t = t;    }
    public T getT()    {        return t;    }}

While creating an instance to the above generic class, you can pass any class type as a type parameter and that class type replaces generic ‘T’ for that object. For example, if you pass String type as a type parameter then String will be the type of variable ‘t’. If you pass Integer as type parameter than Integer will be the type of variable ‘t’.

In the other words, when you pass a type while creating an object to the generic class, that object works only with that type. For example, If you pass String type while creating an object to the above generic class then that object works only with String type. That means setT() method takes String type as an argument and getT() method returns String type. If you pass any other type to setT() method, it gives compile time error. Hence, strictly checking type casting during compilation.

public class GenericsInJava{    public static void main(String[] args)    {        GenericClass<String> gen1 = new GenericClass<String>(“It must be string”);
        gen1.setT(“Value Changed”);        //Passing String to setT() method
        String s = gen1.getT();              //getT() method returning string
        gen1.setT(new Integer(123));      //Compile time error. You can’t pass Integer        type to setT() method now
        gen1.setT(new Double(23.56));    //Compile time error. You can’t pass Double         type to setT() method now    }}

If you create an object by using Integer type as a type parameter then that object works only with Integer type.

public class GenericsInJava{    public static void main(String[] args)    {        GenericClass<Integer> gen1 = new GenericClass<Integer>(new        Integer(123));
        gen1.setT(456);             //Passing Integer type to setT() method
        Integer I = gen1.getT();      //getT() method returning Integer type
        gen1.setT(new String(“123”));      //Compile time error. You can’t pass String       type to setT() method now
        gen1.setT(new Double(23.56));    //Compile time error. You can’t pass Double        type to setT() method now    }}

Generics Work Only With Derived Types :

While creating an instance of generic class, you must pass only derived types. You can’t pass primitive types. If you pass primitive type, it gives compile time error. i.e generics works only with derived type:

GenericClass<int> gen1 = new GenericClass<int>(123);   //Error, can’t use primitive type
        GenericClass<float> gen2 = new GenericClass<float>(23.56);  //Error, can’t use primitive type

Objects Of Same Generic Class Differ Based On Their Type Parameters :

Objects of same generic class differ depending upon their type parameters. For example, object of above generic class created using String type is not compatible with an object of same class created using Integer type.

        GenericClass<String> gen1 = new GenericClass<String>(“Value Of t”);       GenericClass<Integer> gen2 = new GenericClass<Integer>(new Integer(20));
        gen1 = gen2;        //Error : Type mismatch
        gen2 = gen1;        //Error : Type mismatch

Generic Class With Two Type Parameters :


class GenericClass<T1, T2>{    T1 t1;
    T2 t2;
    public GenericClass(T1 t1, T2 t2)    {        this.t1 = t1;
        this.t2 = t2;    }
    public void setT1(T1 t1)    {        this.t1 = t1;    }
    public T1 getT1()    {        return t1;    }
    public void setT2(T2 t2)    {        this.t2 = t2;    }
    public T2 getT2()    {        return t2;    }}
public class GenericsInJava{    public static void main(String[] args)    {        GenericClass<String, Integer> gen1 = new GenericClass<String, Integer>(“Value of t1”, new Integer(123));
        GenericClass<Integer, String> gen2 = new GenericClass<Integer, String>(new Integer(123), “Value of t2”);
        System.out.println(gen1.getT1());       //Output : Value of t1
        System.out.println(gen1.getT2());       //Output : 123
        System.out.println(gen2.getT1());       //Output : 123
        System.out.println(gen2.getT2());       //Output : Value of t2    }}

You can pass your own type while creating an instance to the generic class. Here is an example for that:


class GenericClass<T>{    T t;
    public GenericClass(T t)    {        this.t = t;    }
    public void setT(T t)    {        this.t = t;    }
    public T getT()    {        return t;    }}
class A{    int i;
    public A(int i)    {        this.i = i;    }}
public class GenericsInJava{    public static void main(String[] args)    {        GenericClass<A> gen1 = new GenericClass<A>(new A(10));     //Passing A-type as type parameter
        GenericClass<A> gen2 = new GenericClass<A>(new A(20));     //Passing A-type as type parameter
        System.out.println(gen1.getT().i);    //Output : 10
        System.out.println(gen2.getT().i);    //Output : 20    }}

 Rules To Follow While Implementing Generic Interfaces:

Like generic classes, you can also define generic interfaces. The same syntax used to define generic classes is also used to define generic interfaces. Here is an example of generic interface

interface GenericInterface<T>{    void setT(T t);   
    T getT();}

Rules To Follow While Implementing Generic Interfaces :

Only generic classes can implement generic interfaces. Normal classes can’t implement generic interfaces. For example, above generic interface can be implemented as,

class GenericClass<T> implements GenericInterface<T>{
}Not like below. It gives compile time error.class NormalClass implements GenericInterface<T>{     //Compile time error}Here is the full implementation of above generic interface.Class GenericsClass<T> implements GenericsInterface<T>{Private T t;//Implementing setT() method
    @Override    public void setT(T t)    {        this.t = t;    }
    //Implementing getT() method
    @Override    public T getT()    {        return t;    }}

A normal class can implement a generic interface if type parameter of generic interface is a wrapper class. For example, below implementation of GenericInterface is legal.


interface GenericInterface<Integer>{       //Generic interface with Integer as type parameter}
class NormalClass implements GenericInterface<Integer>{       //Normal class implementing generic interface}

Class implementing generic interface at least must have same number and same type of parameters and at most can have any number and any type of parameters.


interface GenericInterface<T>{    //Generic interface with one type parameter}
class GenericClass1<T> implements GenericInterface<T>{    //Class with same type parameter}
class GenericClass2<T, V> implements GenericInterface<T>{    //Class with two type parameters}
class GenericClass<T1, T2> implements GenericInterface<T>{    //Compile time error, class having different type of parameters}

You can change the type of parameter passed to generic interface while implementing it. When changed, the class which is implementing should have new type as parameter and also, you have to change old type with new type while implementing the methods.


interface GenericInterface<T>{    void setT(T t);
    T getT();}
//Changing the type of parameter passed to GenericInterface while implementing
class GenericClass<V> implements GenericInterface<V>{    V t;
    @Override    public void setT(V t)    //Changing the type of parameter    {        this.t = t;    }
    @Override    public V getT()          //Changing the return type    {        return t;    }}

Generic interface can have any number of type parameters. Class implementing generic interface at least must have  same type of parameters and at most can have any number of parameters:


interface GenericInterface<T1, T2, T3, T4>{    //Generic interface with 4 type parameters}
class GenericClass1<T1, T2, T3, T4, T5> implements GenericInterface<T1, T2, T3, T4>{    //Generic class with 5 type parameters implementing generic interface with 4 type parameters}
class GenericClass2<T1, T2, T3> implements GenericInterface<T1, T2, T3, T4>{    //Compile time error, must have same number of type parameters}
class GenericClass3<T1, T2, T5, T6> implements GenericInterface<T1, T2, T3, T4>{    //Compile time error. must have same type of parameters}

Class can implement more than one generic interfaces. If implemented, class should have type parameters of both the interfaces:


interface GenericInterface1<T1>{    //Generic interface with one type parameter}
interface GenericInterface2<T2, T3>{    //Generic interface with two type parameters}
class GenericClass<T1,T2, T3> implements GenericInterface1<T1>, GenericInterface2<T2, T3>{    //Class having parameters of both the interfaces}

Can We Define Methods And Constructors As Generic?

Generics are very useful and flexible feature of Java. Generics provide safe type casting to your coding. Along with safe type casting, they also give flexibility to your coding. For example, Once you write a class or interface using generics, you can use any type to create objects to them. In simple words, You can make objects to work with any type using generics.

One more addition to generics is Generic Methods. If you don’t want whole class or interface to be generic, you want only some part of class as generic, then generic methods will be solution for this.

The syntax for defining generic methods is as follows,

<type-Parameters> return_type method_name(parameter list){
}
  • You can observe that type parameters are mentioned just before the return type. It is a rule you must follow while defining generic methods. The remaining parts are same as in normal method.
  • Generic methods can be static or non-static. There is no restriction for that. Generic class as well as non-generic class can have generic methods.
class NonGenericClass{       static <T> void genericMethod(T t1)    {        T t2 = t1;                 System.out.println(t2);    }}
  •  In this example, ‘genericMethod()’ is a static generic method with ‘T’ as type parameter. Notice that type parameter is mentioned just before the return type.
  • While calling above generic method, you can pass any type as an argument. This is the best example for generics providing the flexibility. Look at the below code, I have called the above method by passing three different types as an argument.

public class GenericsInJava{    public static void main(String[] args)    {        NonGenericClass.genericMethod(new Integer(123));     //Passing Integer type as an argument                 NonGenericClass.genericMethod(“I am string”);        //Passing String type as an argument                 NonGenericClass.genericMethod(new Double(25.89));    //Passing Double type as an argument    }}

Constructors As Generics :

As we all know that constructors are like methods but without return types. Like methods, constructors also can be generic. Even non-generic class can have generic constructors. Here is an example in which constructor of a non-generic class is defined as generic.



class NonGenericClass
{       public <T> NonGenericClass(T t1)    {        T t2 = t1;                 System.out.println(t2);    }}
public class GenericsInJava{    public static void main(String[] args)    {        //Creating object by passing Integer as an argument                 NonGenericClass nonGen1 = new NonGenericClass(123);                 //Creating object by passing String as an argument                 NonGenericClass nonGen2 = new NonGenericClass(“abc”);                 //Creating object by passing Double as an argument                 NonGenericClass nonGen3 = new NonGenericClass(25.69);    }}

What Are Bounded Types And Why They Are Used?

  • In the earlier posts, We have seen that while creating objects to generic classes we can pass any derived type as type parameters. Many times it will be useful to limit the types that can be passed to type parameters. For that purpose, bounded types or bounded type parameters are introduced in generics. Using bounded types, you can make the objects of generic class to have data of specific derived types.
  • For example, If you want a generic class that works only with numbers (like int, double, float, long …..) then declare type parameter of that class as a bounded type to java.lang.Number class. Then while creating objects to that class you have to pass only Number types or it’s subclass types as type parameters.

Here is the syntax for declaring Bounded type parameters.

<T extends SuperClass>

This specifies that ‘T’ can only be replaced by ‘SuperClass’ or it’s sub classes. Remember that extends clause is an inclusive bound. That means bound includes ‘SuperClass’ also.

Here is an example which demonstrates the bounded type parameters

class GenericClass<T extends Number>    //Declaring Number class as upper bound of T{    T t;
    public GenericClass(T t)    {        this.t = t;    }
    public T getT()    {        return t;    }}

In this example, T has been declared as bounded type to Number class. So while creating objects to this class, you have to pass either Number type or it’s subclass types (Integer, Double, Float, Byte… ) as a type parameter. It wouldn’t allow other than these types to pass as a type parameter. If you try to pass, compiler will throw compile time error.

public class GenericsInJava{    public static void main(String[] args)    {        //Creating object by passing Number as a type parameter
        GenericClass<Number> gen1 = new GenericClass<Number>(123);
        //Creating object by passing Integer as a type parameter
        GenericClass<Integer> gen2 = new GenericClass<Integer>(new Integer(456));
        //Creating object by passing Double as a type parameter
        GenericClass<Double> gen3 = new GenericClass<Double>(new Double(23.589));
        //Creating object by passing Long as a type parameter
        GenericClass<Long> gen4 = new GenericClass<Long>(new Long(12));
        //While Creating object by passing String as a type parameter, it gives compile time error
        GenericClass<String> gen5 = new GenericClass<String>(“I am string”);   //Compile time error    }}

Bounded Type Parameters In Generic Methods :

You can use bounded types while defining generic methods also.

Here is an example.

class GenericClass{    //Declaring T as bounded type to Number class
    public static <T extends Number> void printNumbers(T[] t)    {        for (int i = 0; i < t.length; i++)        {            System.out.println(t[i]);        }    }}
public class GenericsInJava{    public static void main(String[] args)    {        //Passing Integer[] array while calling printNumbers()
        GenericClass.printNumbers(new Integer[] {new Integer(10), new Integer(20), new Integer(30), new Integer(40)} );
        //Passing Double[] array while calling printNumbers()
        GenericClass.printNumbers(new Double[] {new Double(21.45), new Double(20.45), new Double(34.87), new Double(48.36)} );
        //Passing String[] array while calling printNumbers(), it gives compile time error
        GenericClass.printNumbers(new String[] {“one”, “Two”, “Three”, “Four”});    //Compile time error    }}

 Using Interface As An Upper Bound :

You can also use interface type along with class type as an upper bound to type parameters. As in java, any class can extend only one class and can implement multiple interfaces, this also applies while declaring the bound to type parameters. That means a bounded parameter can extend only one class and one or more interfaces. While specifying bounded parameters that has a class and an interface or multiple interfaces, use & operator as a delimiter.

class GenericClass <T extends AnyClass & FirstInterface & SecondInterface>{   
}


What Are Wildcard Arguments In Java?

Wildcard arguments means unknown type arguments. They just act as placeholder for real arguments to be passed while calling method. They are denoted by question mark (?). One important thing is that the types which are used to declare wildcard arguments must be generic types. Wildcard arguments are declared in three ways.

1) Wildcard Arguments With An Unknown Type

2) Wildcard Arguments with An Upper Bound

3) Wildcard Arguments with Lower Bound

Wildcard Arguments With An Unknown Type :

The syntax for declaring this type of wildcard arguments is,

GenericType<?>


The arguments which are declared like this can hold any type of objects. For example, Collection<?> or ArrayList<?> can hold any type of objects like String, Integer, Double etc.

Look at the below code. The same processElements() method is used to process the ArrayList containing strings as well as integers.

public class GenericsInJava{    static void processElements(ArrayList<?> a)    {        for (Object element : a)        {            System.out.println(element);        }    }
    public static void main(String[] args)    {        //ArrayList Containing Integers
        ArrayList<Integer> a1 = new ArrayList<>();
        a1.add(10);
        a1.add(20);
        a1.add(30);
        processElements(a1);
        //Arraylist containing strings
        ArrayList<String> a2 = new ArrayList<>();
        a2.add(“One”);
        a2.add(“Two”);
        a2.add(“Three”);
        processElements(a2);    }}

Wildcard Arguments With An Upper Bound :

  •  In the above example, if you want the processElements() method to work with only numbers, then you can specify an upper bound for wildcard argument. To specify an upper bound for wildcards, use this syntax,

v  GenericType<? extends SuperClass>

  •  This specifies that a wildcard argument can contain ‘SuperClass’ type or it’s sub classes. Remember that extends clause is an inclusive bound. i.e ‘SuperClass’ also lies in the bound.
  •  The above processElements() method can be modified to process only numbers like below,

public class GenericsInJava{    static void processElements(ArrayList<? extends Number> a)    {        for (Object element : a)        {            System.out.println(element);        }    }
    public static void main(String[] args)    {        //ArrayList Containing Integers
        ArrayList<Integer> a1 = new ArrayList<>();
        a1.add(10);
        a1.add(20);
        a1.add(30);
        processElements(a1);
        //Arraylist containing Doubles
        ArrayList<Double> a2 = new ArrayList<>();
        a2.add(21.35);
        a2.add(56.47);
        a2.add(78.12);
        processElements(a2);
        //Arraylist containing Strings
        ArrayList<String> a3 = new ArrayList<>();
        a3.add(“One”);
        a3.add(“Two”);
        a3.add(“Three”);
        //This will not work
        processElements(a3);     //Compile time error    }}

Wildcard Arguments With Lower Bound 

  • You can also specify a lower bound for wildcard argument using super clause. Here is the syntax,

v  GenericType<? super SubClass>

  • This means that a wildcard argument can contain ‘SubClass’ type or it’s super classes.

public class GenericsInJava{    static void processElements(ArrayList<? super Integer> a)    {        for (Object element : a)        {            System.out.println(element);        }    }
    public static void main(String[] args)    {        //ArrayList Containing Integers
        ArrayList<Integer> a1 = new ArrayList<>();
        a1.add(10);
        a1.add(20);
        a1.add(30);
        processElements(a1);
        //Arraylist containing Doubles
        ArrayList<Double> a2 = new ArrayList<>();
        a2.add(21.35);
        a2.add(56.47);
        a2.add(78.12);
        //This will not work
        processElements(a2);     //Compile time error    }}

Note : ‘super’ clause is used to specify the lower bound for only wildcard arguments. It does not work with bounded types.

Generics And Their Inheritance

You have to follow some rules while making a generic class as a super class or a sub class. Some of those rules we have already discussed while implementing generic interfaces. This post is an extend of that post.

In this post, We will discuss some very interesting points about generic classes and their inheritance.

A generic class can extend a non-generic class:


class NonGenericClass{     //Non Generic Class}
class GenericClass<T> extends NonGenericClass{    //Generic class extending non-generic class}
  • Generic class can also extend another generic class. When generic class extends another generic class, sub class should have at least same type and same number of type parameters and at most can have any number and any type of parameters.
  • When generic class extends another generic class, the type parameters are passed from sub class to super class same as in the case of constructor chaining where super class constructor is called by sub class constructor by passing required arguments. For example, in the below program  ‘T’ in ‘GenericSuperClass’ will be replaced by String.

class GenericSuperClass<T>{    T t;
    public GenericSuperClass(T t)    {        this.t = t;    }}
class GenericSubClass<T> extends GenericSuperClass<T>{    public GenericSubClass(T t)    {        super(t);    }}
public class GenericsInJava{    public static void main(String[] args)    {        GenericSubClass<String> gen = new GenericSubClass<String>(“I am string”);
        System.out.println(gen.t);       //Output : I am string    }}
  • A generic class can extend only one generic class and one or more generic interfaces. Then it’s type parameters should be union of type parameters of generic class and generic interface(s).

 Non-generic class can’t extend generic class except of those generic classes which have already pre defined types as their type parameters.

Non-generic class can extend generic class by removing the type parameters. i.e as a raw type. But, it gives a warning.


class GenericClass<T>{    T t;
    public GenericClass(T t)    {        this.t = t;    }}
class NonGenericClass extends GenericClass       //Warning{    public NonGenericClass(String s)    {        super(s);           //Warning    }}
public class GenericsInJava{    public static void main(String[] args)    {        NonGenericClass nonGen = new NonGenericClass(“I am String”);
        System.out.println(nonGen.t);    //Output : I am String    }}

While extending a generic class having bounded type parameter, type parameter must be replaced by either upper bound or it’s sub classes.


class GenericSuperClass<T extends Number>{    //Generic super class with bounded type parameter}
class GenericSubClass1 extends GenericSuperClass<Number>{    //type parameter replaced by upper bound}
class GenericSubClass2 extends GenericSuperClass<Integer>{    //type parameter replaced by sub class of upper bound}
class GenericSubClass3 extends GenericSuperClass<T extends Number>{    //Compile time error}

Generic methods of super class can be overrided in the sub class like normal methods