Generics

Vipul Gupta
5 min readApr 26, 2021

--

Here are the notes I made after studying the Generics concept in java.

GENERICS:
1. Introduction.
2. Generic Classes.
3. Bounded Types.
4. Generic Methods and Wild Card Characters.
5. Communication with non-generic code.
6. Conclusions.

Generics: The main Objective of Generics is
1. To provide type-safety and
2. To resolve the type-casting problem.
______________________________________________________________________________________________________________________________________________
1. Introduction:

Type-casting is a headache in the collection, So to overcome this SUN people introduces the Generics concept in the 1.5 version.

Type-Safety:
Arrays are type-safe we can give the guarantee of type of elements present inside the array(except objects type)
Ex: If we want String type array, We do
Stirng[] str = new String[100];
So we can’t add any other type of object else get a compile-time error.

Collections are not type-safe, we can’t guarantee objects inside collections. Here we can add any type at compile time with no problem, but at runtime, it will fail.
ArrayList list = new ArrayList();
list.add(“Vipul”);
list.add(1);

Type-Casting:
In the case of Array at the time of retrieval, we are not required to do type-casting, bcz there is the guarantee for a type of element present inside an array.
String s1 = str.get(1);
String s2 = str.get(0);

But in the case of Collections, at the time of retrieval, we are required to do type-casting, bcz there is no guarantee for the type of element present inside the array.

ArrayList list = new ArrayList();
list.add(“Vipul”);
list.add(1);
String s1 = (String)list.get(0);// type-casting is mandatory.

So, After Generics is become : ArrayList<String> list = new ArrayList<String>();

Conslutions:
1. Polymorphism concept only applicable for Base type not for parameter type.

List<String> list = new ArrayList<String>();
Collection<String> list = new ArrayList<String>();

ArrayList<Object> list = new ArrayList<String>(); X
Here base is ArrayList and type is Object and String.

2. For type we can provide any class or interface name but not a primitive type.
ArrayList<int> list = new ArrayList<int>(); X

______________________________________________________________________________________________________________________________________________

2. Generic Classes.

class ArrayList<T>{
add(T t);
T get(int index);
}
- Based on our Runtime T will be replaced with our provided type.

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

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

Here is example of Generic class:

class Gen<T> {
T obj;

Gen(T obj) {
this.obj = obj;
}

public void show() {
System.out.println(“The type of obj :” + obj.getClass().getName());
}

public T getObject() {
return obj;
}

}

public class Generics {

public static void main(String[] args) {

Gen<String> g1 = new Gen<String>(“Vipul”);
g1.show();
System.out.println(g1.getObject());

Gen<Integer> g2 = new Gen<Integer>(1);
g2.show();
System.out.println(g2.getObject());

Gen<Double> g3 = new Gen<Double>(1.5);
g3.show();
System.out.println(g3.getObject());

}

}

______________________________________________________________________________________________________________________________________________
3. Bounded Types:
- We can bound the type parameter for a particular range by using extends keywords such type is called bounded types.
- We can define bounded type only by using extends keyword. (for implements type also use extends keyword)
- Based on the requirement we can take any number of parameters separated by a comma.(class Gen<key,value>{}, also HashMap<key,value>)

Here the class is the unbounded type we can pass any parameter and it will become that type.

say: class Gen<T>{
}
Gen<Integer> g1 = new Gen<Integer>();
Gen<String> g1 = new Gen<String>();

So to make it bounded type :
class Gen<T extends X>{
}
X: it can either be class or interface, if it is class then as a type parameter we can pass either x type or its child type.
X: if it is the interface we can pass either X type or its implementation classes.

Ex:1
class Gen<T extends Number>{
}
Gen<Integer> g1 = new Gen<Integer>();
Gen<Float> g1 = new Gen<Float>();

Gen<String> g1 = new Gen<String>(); X this will not work as type is number. Compile time error
__________________________________________________
Ex: 2
class Gen<T extends Runnable>{
}
Gen<Runnable> g1 = new Gen<Runnable>();
Gen<Thread> g1 = new Gen<Thread>();

Gen<Integer> g1 = new Gen<Integer>();X this will not work as type is Runnable. Compile time error

______________________________________________________________________________________________________________________________________________
4. Generic Method and Wild-card characters:

1. m1(ArrayList<String> l)
m1(ArrayList<String> l){

}
- We can call this method by passing arraylist of only String type, otherwise we get Compiletime error.
m1(ArrayList<String> l){
l.add(“abc”);
l.add(1); // CE:
}

2. m1(ArrayList<?> l) : (best suitable for read-only operation)
- We can call this method by passing arraylist of any type, but within the method we cannot add anything to the list except null, bcz we don’t know the type.
null is allowed bcz it is valid value of any type.
m1(ArrayList<?> l){
l.add(“abc”); X
l.add(1);
l.add(null);
}
ArrayList<?> list = new ArrayList<Integer>();
ArrayList<?> list = new ArrayList<String>();

3. m1(ArrayList<? extends X> l)
- X can either be class or interface.

ArrayList<? extends Number> list = new ArrayList<Integer>();
ArrayList<?extends Number> list = new ArrayList<String>(); // CE:

4. m1(ArrayList<? Super X> l)
- X can either be class or interface.
- X is class then we can call by ArrayList of either x type or its superclass.
- X is the interface then we can call this method by x type or superclass implementation of a class of x. (Runnable -> Thread -> Object)
- We can add X type and null type only.

ArrayList<? Super String> l = new ArrayList<Object>();

We can define bounded type either at class level or method level.

Class Level.
class Test<T>{

}
Method Level.
class Test{
public <T> void m1(T ob){

}
}

Here T can be:
1. <T extends Number>
2. <T extends Runnable>
3. <T extends Number & Runnable>
4. <T extends Number & Comparable>
5. <T extends Number & Thread>

______________________________________________________________________________________________________________________________________________

5. Communication with Non-Generic Code.
if we send Generic code to a non-generic area it starts behaving like non-generic and vice-versa.

Generics main purpose is to provide type-safety and type-casting, these both concepts are appliable at Compile time hence Generic concept also
applicable at compile-time, not runtime.
- At the time of compilation at last Generic syntax will be removed and hence for JVM Generic syntax won’t be available.
Hence all these declarations are equal.

ArrayList list = new ArrayList<Integer>();
ArrayList list = new ArrayList<String>();
ArrayList list = new ArrayList<Double>();
ArrayList list = new ArrayList();

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

// Here both are having same signature and we get CE: both signature are same.

class Test{
public void m1 (ArrayList<Integer> list){}
public void m1 (ArrayList<String> list){}
}

Sources: DurgaSoft, Stackoverflow, internet….

Hope this will help in understanding the concept of Generics in java.

Happy Coding!!

--

--

Vipul Gupta
Vipul Gupta

No responses yet