Sitemap
Vignesh

Vignesh

Generics in Java

5 min readSep 24, 2026

--

Press enter or click to view image in full size

Why Generics?

Generics allows us to write classes, methods and interfaces that works with multiple data types, without having to specify the exact type in advance.

Generics only accept Wrapper class types (because of a process called type erasure) not primitives.

key Features

  • Code Reusability: We can use the same class or methods that works with different data types.
  • Type Safety: It catches the errors at compile time instead of runtime.
  • Elimination of Casting: It removes the explicit type casting when retrieving items.

Generic type Naming convention

  • E — Element (used in Collection)
  • K — Key (used in Map)
  • V — Value (used in Map)
  • T — Type
  • S, U, V — 2nd, 3rd, 4th types

Generic Method in Java

Generic method is a method that works with different data types.

  1. Method with no return type
public class Demo {
public static <T> void printValue(T value){
System.out.println("Value : "+value);
}
public static void main(String[] args) {
printValue(10); //T = Integer
printValue("Vignesh"); //T = String
printValue(10.3); //T = Double
}
}

Output:

Value : 10
Value : Vignesh
Value : 10.3

2. Method with return type

public class Demo {
public static<T> T returnValue(T value){
return value;
}
public static void main(String[] args) {
String name=returnValue("Vignesh");
System.out.println(name);
System.out.println(returnValue(10));
System.out.println(returnValue(10.5));
}
}
Output:
Vignesh
10
10.5

Generic class in Java

Similarly generic class also that works with different types.

please first go through once the below code, then only you can able to understand Diamond operator and Type erasure.

Diamond Operator <>

Without Diamond operator :

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

without diamond operator we have to write String twice.

With Diamond operator:

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

with diamond operator, java automatically identifies the type while the compile time.

Type erasure

what is type erasure?

Java use generic type information during compilation, but the generic type is automatically removed/erased from the compiled byte code. So runtime java don’t have any idea about T(type parameter).

Java asks, hey i don’t know any idea about type T, what it means. That’s why we used Object instead of type T.

Why we need to use Object instead T?

As we said before, java don’t have any idea about T at runtime. So we used Object, because object is a superclass of all reference types, so it can hold objects of different reference types.

private T[] data;
data = new T[10]; // this is not allowed 🙅‍♀️ cause actual array creation happen at runtime.

//since T is erased and its actual type is not available on runtime we use Object

Object[] data= new Object[size];

Generic class example :

public class Demo {
public static void main(String[] args) {
CustomArrayList<Integer> list=new CustomArrayList<>(5);
list.add(10);
list.add(20);
list.addFirst(5);
list.printList();
System.out.println(list.isFull());
System.out.println(list.isEmpty());
}
}
public class CustomArrayList<T>{
private Object[] data;
protected static final int DEFAULT_SIZE = 10;
private int size=0;


public CustomArrayList() {
this(DEFAULT_SIZE);
}
public CustomArrayList(int size) {
data = new Object[size];
}

public void add(T value){
if(isFull()){
doubleArray();
}
data[size++]=value;
}
public void addFirst(T value){
if (isFull()) {
doubleArray();
}
size++;
for(int i=size-1;i>0;i--){
data[i]=data[i-1];
}
data[0]=value;
}
public T get(int index){
return (T) data[index]; // Here we need to cast, cause data is Object[],
// so data[index] is Object but we said return T, So thats we casted into T.
}
public void set(int index,T value){
data[index]=value;
}

private void doubleArray(){
Object[] array=new Object[data.length*2];
for(int i=0;i<data.length;i++){
array[i]=data[i];
}
data=array;
}
public T deleteLast() throws Exception{
if(isEmpty()){
throw new Exception("Array is Emptyp");
}
return (T) data[--size];
}

public boolean isEmpty(){
return size==0;
}
public boolean isFull(){
return size==data.length;
}

public void printList(){
System.out.print("[");
for(int i=0;i<size;i++){
if(i==size-1){
System.out.print(data[i]);
}
else{
System.out.print(data[i]+",");
}
}
System.out.println("]");
}
}
OUTPUT :
[5,10,20]
false
false

Wildcard ? in Generics

Get Vignesh’s stories in your inbox

Join Medium for free to get updates from this writer.

If you don’t know the exact generic type, we can use ? . It accepts all the types.

?which means unknown type.

For instance :

List<?> list

Why do we need ? ?

suppose you have:

List<String> names = new ArrayList<>();
List<Integer> numbers = new ArrayList<>();

you want one method that accepts both.

public void printList(List<?> list) {
for (Object value : list) {// here Object is a parent class of all reference
System.out.println(value);
}
}

Important Limitation

List<?> list = new ArrayList<String>(); 
list.add("Hello"); // this is not allowed, cause java doesn't know
//what ? actually represents.

Upper Bounded Wildcards

Sometimes we need to restrict some types and you only want to allow specific types like Number and its subclass type, that time we can use upper bounded wildcard.

List<? extends Number>

This means List can contain Number and it subclass like Integer, Double, Float and Long.

public class Demo {

public static void printNumbers(List<? extends Number> numbers) {
for (Number number : numbers) {
System.out.println(number);
}
}

public static void main(String[] args) {

List<Integer> integers = Arrays.asList(10, 20, 30);
List<Double> doubles = Arrays.asList(10.5, 20.5, 30.5);

printNumbers(integers);
printNumbers(doubles);
}
}
OUTPUT:
10
20
30
10.5
20.5
30.5

Lower Bounded Wildcards

For lower bounded wildcard we use super keyword.

List<? super Integer>

This means the List must be Integer and its superclass of Integer.

Object
↑
Number
↑
Integer

// So these below are only allowed.
List<Integer>
List<Number>
List<Object>

//these below are not allowed.
List<Double>
List<String>
public class Demo {
public static void addNumbers(List<? super Integer> numbers) {
numbers.add(10);
numbers.add(20);
numbers.add(30);

System.out.println(numbers);
}

public static void main(String[] args) {

List<Integer> integers = new ArrayList<>();
addNumbers(integers);
}
}
OUTPUT:
[10, 20, 30]

Producer and Consumer

While working with Wildcards we need to understand what is producer? and what is consumer?

  • Producer — means produce data, give some data. In programming we print something, which means we are reading the data(read panrathu) not write. We use extends(List<? extends Number>) keyword.
  • Consumer — means consumes/accepts data, used to write the data. Here we use super keyword. We use add method to write the data in array.
  1. Producer — extends

As we said, producer is something that produces/gives us values.

Producer → ? extends 
↓
READ

For instance:

public static void printNumbers(List<? extends Number> numbers) { 
for (Number number : numbers) {
System.out.println(number);
}
}

here list is producer cause we are getting the values. We are mainly reading the data.

2. Consumer — super

Consumer is something that accepts/receives values.

Consumer → ? super 
↓
ADD / WRITE

For instance :

public static void addNumbers(List<? super Integer> numbers) {
numbers.add(10);
numbers.add(20);
}

Here we used add method to write the data in the list.

Easy way to remember

PECS

  • Producer — Extends
  • Consumer — Super

If you found this blog is useful to you, please don’t forget to hit the like button. That will motivates me to write more blogs like this.

நன்றி 🙏😁…

--

--