Avijit Pramanik's Blog

Avijit Pramanik's Blog

Java Generics Example

What is generics?

Generics force you to use a specific type (Class or Interface) when defining your Interface/Class or Methods.

Imagine a scenario where you have to add few Objects of a class to an ArrayList and then you want to retrieve and get the value out of it. How can that be done?

Customer.Java

package com.avijit.blog;

public class Customer {
    private String name;
    private String address;
    
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getAddress() {
        return address;
    }
    public void setAddress(String address) {
        this.address = address;
    }
}

TestCustomer.java

package com.avijit.blog;

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

public class TestCustomer {
    public static void main(String[] args) {
        List list=new ArrayList();
        
        //Adding two objects of Customer to the list
        list.add(new Customer("Name1", "Address1"));
        list.add(new Customer("Name2", "Address2"));
        
        /* Fetching the first one and to do that we have to Cast the object
        since in runtime the list.get(0) doesn't have any idea about what 
        type of object is stored.
        */
        Customer c=(Customer)list.get(0);
        System.out.println(c.getName() + " " + c.getAddress());
        
    }
}

In the above example we can see that we have to use Cast to access the Object and Cast is an expensive operation in terms of JVM and we should want to avoid this as mush as we can.

How can this be achieved by using Generics and without Casting?

TestCustomer.java

package com.avijit.blog;

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

public class TestCustomer {
    public static void main(String[] args) {
        // The list can now only hold the Customer type of Object;
        List<Customer> list=new ArrayList<Customer>();
        
        //Adding two objects of Customer to the list
        list.add(new Customer("Name1", "Address1"));
        list.add(new Customer("Name2", "Address2"));
        
        /* Since the Customer type of object can only be hold 
         * by list object, thats why there is no need to 
         * explicit casting as the Customer type
         * of object is expected.
        */
        Customer c=list.get(0);
        System.out.println(c.getName() + " " + c.getAddress());
        
    }
}

How to use a specific Type in Classes?

Sometimes we need to use functionality for handling different types from a class. For example, following class can be used to hold any type of list.
CustomList.java
package com.avijit.blog;

import java.util.List;
/**
 *This class can be used to hold any specific type as 
 *list and later the entire list can be accessed
 *
 * @param <T>
 */
public class CustomList{
    
    private List<T> list;
    
    public List<T> getList() {
        return list;
    }
    
    public void setList(List<T> list) {
        this.list = list;
    }
    
    public void addToList(T t){
        list.add(t);
    }
}

TestCustomList.java

package com.avijit.blog;

public class TestCustomList {

    public static void main(String[] args) {
        //CustomList can hold a list of Customer object and then
        // it can return that when required.
        CustomList<Customer> cl=new CustomList<Customer>();
        
        cl.addToList(new Customer("Name1", "Address1"));
        cl.addToList(new Customer("Name2", "Address2"));
        
        for(Customer c:cl.getList()){
            System.out.println(c.getName() + " " + c.getAddress());
        }
    }

}

How to use a generic bounded Type in Method?

OnlineCustomer.java
package com.avijit.blog;

/**
 * OnlineCustomer class is extended from Customer class.
 * So all the public method of Customer is accessible 
 * in this class. super keyword is used to inject
 * values to the Customer class's constructor.
 * 
 * @author Avijit
 *
 */
public class OnlineCustomer extends Customer {

    public OnlineCustomer(String name, String address) {
        super(name, address);
        
    }
}

TestGenericsMethod.java

package com.avijit.blog;

public class TestGenericsMethod {
    public static void main(String[] args) {
        OnlineCustomer onlineCustomer=new OnlineCustomer("name1","address1");
        System.out.println(getCustomerName(onlineCustomer));
    }
    
    /*
     * Generic method which can take any instance of class Customer or 
     * instance of any class derived from it. Since the 
     * OnlineCustomer class is derived from Customer so the
     * getCustomerName function can return the output of the 
     * getName function from the class Customer.
     */
    public static <T extends Customer> String getCustomerName(T t){
        return t.getName();
    }
}

Wildcard Upper Bound and Lower Bound Example

Mostly the wildcard (?) is used in generics on a variable to make it relaxed and accept Upper Bound or Lower Bound types. It can also except Unbound wildcards. Lets analyze the following examples on these different kind of Wildcard approach.

WildcardExample.java

package com.avijit.blog;

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

public class WildcardExample {
    public static void main(String[] args) {
        List listOnlineCustomer=new ArrayList();
        listOnlineCustomer.add(new OnlineCustomer("Name1", "Address1"));
        listOnlineCustomer.add(new OnlineCustomer("Name2", "Address2"));
        printCustomerName1(listOnlineCustomer);
        
        List listCustomer=new ArrayList();
        listCustomer.add(new Customer("Name3", "Address3"));
        listCustomer.add(new Customer("Name4", "Address4"));
        printCustomerName2(listCustomer);
        
        List listCustomerUnbound=new ArrayList();
        listCustomerUnbound.add(new Customer("Name5", "Address5"));
        listCustomerUnbound.add(new Customer("Name6", "Address6"));
        printCustomerName3(listCustomerUnbound);
    }
    
    //Upper Bound:- Customer type or derived from it
    public static void printCustomerName1(List<? extends Customer> customerList){
        for(Customer c:customerList){
            System.out.println(c.getName());
        }
    }
    
    //Lower Bound:- OnlineCustomer or Super type of that class
    public static void printCustomerName2(List<? super OnlineCustomer> customerList){
        for(Object obj:customerList){
            Customer c=(Customer)obj;
            System.out.println(c.getName());
        }
    }
    
    // Unbound:- Any Unknown Type
    public static void printCustomerName3(List<?> customerList){
        
        //This can only be done in Unbound Wildcard
        customerList.add(null);
        for(Object obj:customerList){
            if(obj !=null){
            Customer c=(Customer)obj;
            System.out.println(c.getName());
            }
        }
    }
}