0% found this document useful (0 votes)
4 views

Unit 4 Cs3452 Java 1

This document covers the concepts of threads and generic classes in Java, focusing on multithreading, thread life cycle, thread priorities, and synchronization. It explains the differences between multithreading and multitasking, the creation of threads via Runnable interface and Thread class, and the importance of thread synchronization to manage shared resources. Additionally, it discusses inter-thread communication, daemon threads, and the use of thread groups for managing multiple threads.

Uploaded by

godwinraj2006
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOC, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
4 views

Unit 4 Cs3452 Java 1

This document covers the concepts of threads and generic classes in Java, focusing on multithreading, thread life cycle, thread priorities, and synchronization. It explains the differences between multithreading and multitasking, the creation of threads via Runnable interface and Thread class, and the importance of thread synchronization to manage shared resources. Additionally, it discusses inter-thread communication, daemon threads, and the use of thread groups for managing multiple threads.

Uploaded by

godwinraj2006
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOC, PDF, TXT or read online on Scribd
You are on page 1/ 24

CS3452 - JAVA PROGRAMMING

UNIT IV THREADS AND GENERIC CLASSES

Understanding Threads, Needs of Multi-Threaded Programming, Thread Life-Cycle,


Thread Priorities, Synchronizing Threads, Inter Communication of Threads, Critical
Factor in Thread – Deadlock-Generic: Introduction to Generics-Built-in Generics
collections-writing simple generic class.

Thread :
A thread is a single sequential( separate ) flow of control within program. Sometimes, it is called
an execution context or light weight process.

Multithreading:
Multithreading is a conceptual programming concept where a program (process) is divided into
two or more subprograms (process), which can be implemented at the same time in parallel. A
multithreaded program contains two or more parts that can run concurrently. Each part of such a
program is called a thread, and each thread defines a separate path of execution.

Multitasking:
Executing several tasks simultaneously is called multi-tasking.
There are 2 types of multi-tasking
1. Process-based multitasking
2. Thread-based multi-tasking

1. Process-based multi-tasking
Executing various jobs together where each job is a separate independent operation, is
called process-based multi-tasking.

2. Thread-based multi-tasking

Executing several tasks simultaneously where each task is a separate independent part of
the same program is called Thread-based multitasking and each independent part is called
Thread. It is best suitable for the programmatic level. The main goal of multi-tasking is to make
or do a better performance of the system by reducing response time
Multithreading vs Multitasking
Multithreading is to execute multiple Multitasking is to run multiple processes on
threads in a process concurrently. a computer concurrently.
Execution
In Multithreading, the CPU switches In Multitasking, the CPU switches between
between multiple threads in the same multiple processes to complete the
process. execution.
Resource Sharing
In Multithreading, resources are shared In Multitasking, resources are shared
among multiple threads in a process. among multiple processes.
Complexity
Multithreading is light-weight and easy to Multitasking is heavy-weight and harder to
create. create.

Life Cycle of Thread


A thread can be in any of the five following states
1.Newborn State:
When a thread object is created a new thread is born and said to be in Newborn state.
2.Runnable State:
If a thread is in this state it means that the thread is ready for execution and waiting for the
availability of the processor. If all threads in queue are of same priority then they are given time
slots for execution in round robin fashion
3.Running State:
It means that the processor has given its time to the thread for execution. A thread keeps
running until the following conditions occurs
a.Thread give up its control on its own and it can happen in the following situations
i.A thread gets suspended using suspend() method which can only be revived with
resume() method
ii.A thread is made to sleep for a specified period of time using sleep(time) method,
where time in iii.A thread is made to wait for some event to occur using wait () method.
In this case a thread can be scheduled to run again using notify () method.
b.A thread is pre-empted by a higher priority thread
4.Blocked State:
If a thread is prevented from entering into runnable state and subsequently running state,
then a thread is said to be in Blocked state.
5.Dead State:
A runnable thread enters the Dead or terminated state when it completes its task or otherwise.

The Main Thread


When we run any java program, the program begins to execute its code starting from the
main method. Therefore, the JVM creates a thread to start executing the code present in main
method. This thread is called as main thread. Although the main thread is automatically created,
you can control it by obtaining a reference to it by calling currentThread() method.

Two important things to know about main thread are,


 It is the thread from which other threads will be produced.
 main thread must be always the last thread to finish execution.
class MainThread
{
public static void main(String[] args)
{
Thread t1=Thread.currentThread();
t1.setName("MainThread");
System.out.println("Name of thread is "+t1);
}
}
Output:
Name of thread is Thread[MainThread,5,main]
CREATION OF THREAD
Thread Can Be Implemented In Two Ways
1) Implementing Runnable Interface
2) Extending Thread Class

1.Create Thread by Implementing Runnable


The easiest way to create a thread is to create a class that implements the Runnable
interface. To implement Runnable, a class need only implement a single method called run()

Example:
public class ThreadSample implements Runnable
{

public void run()


{
try
{
for (int i = 5; i > 0; i--)
{
System.out.println("Child Thread" + i);
Thread.sleep(1000);
}
}
catch (InterruptedException e)
{
System.out.println("Child interrupted");
}
System.out.println("Exiting Child Thread");
}
}
public class MainThread
{
public static void main(String[] arg)
{
ThreadSample d = new ThreadSample();
Thread s = new Thread(d);
s.start();
try
{
for (int i = 5; i > 0; i--)
{
System.out.println("Main Thread" + i);
Thread.sleep(5000);
}
}
catch (InterruptedException e)
{
System.out.println("Main interrupted");
}
System.out.println("Exiting Main Thread");
}}
2. Extending Thread Class
The second way to create a thread is to create a new class that extends Thread, and then to
create an instance of that class. The extending class must override the run( ) method, which is the
entry point for the new thread. It must also call start( ) to begin execution of the new thread.
Example:
public class ThreadSample extends Thread
{
public void run()
{
try
{
for (int i = 5; i > 0; i--)
{
System.out.println("Child Thread" + i);
Thread.sleep(1000);
}
}
catch (InterruptedException e)
{
System.out.println("Child interrupted");
}
System.out.println("Exiting Child Thread");
}
}
public class MainThread
{
public static void main(String[] arg)
{
ThreadSample d = new ThreadSample();
d.start();
try
{
for (int i = 5; i > 0; i--)
{
System.out.println("Main Thread" + i);
Thread.sleep(5000);
}
}
catch (InterruptedException e)
{
System.out.println("Main interrupted");
}
System.out.println("Exiting Main Thread");
}
}

THREAD PRIORITY:
Each thread have a priority. Priorities are represented by a number between 1
and 10. In most cases, thread schedular schedules the threads according to their priority
(known as preemptive scheduling). But it is not guaranteed because it depends on JVM
specification that which scheduling it chooses.
3 constants defined in Thread class:
public static int MIN_PRIORITY
public static int NORM_PRIORITY
public static int MAX_PRIORITY
Default priority of a thread is 5 (NORM_PRIORITY). The value of MIN_PRIORITY
is 1 and the value of MAX_PRIORITY is 10.
Example :
public class MyThread1 extends Thread {
MyThread1(String s)
{
super(s);
start();
}
public void run()
{
for(int i=0;i<5;i++)
{
Thread cur=Thread.currentThread();
cur.setPriority(Thread.MAX_PRIORITY);
int p=cur.getPriority();
System.out.println("Thread Name"+Thread.currentThread().getName());
System.out.println("Thread Priority"+cur);
}
}
}

class MyThread2 extends Thread {


MyThread2(String s)
{
super(s);
start();
}
public void run()
{
for(int i=0;i<5;i++)
{
Thread cur=Thread.currentThread();
cur.setPriority(Thread.MIN_PRIORITY);
System.out.println(cur.getPriority());
int p=cur.getPriority();
System.out.println("Thread Name"+Thread.currentThread().getName());
System.out.println("Thread Priority"+cur);
}
}
}

public class ThreadPriority {


public static void main(String[] args)
{
MyThread1 m1=new MyThread1("MyThread1");
MyThread2 m2=new MyThread2("MyThread2");
}
}

SYNCHRONIZING THREADS
 Synchronization in java is the capability to control the access of multiple threads to any
shared resource.
 Java Synchronization is better option where we want to allow only one thread to access
the shared resource

General Syntax :
synchronized(object)
{
//statement to be synchronized
}

Why use Synchronization


The synchronization is mainly used to
 To prevent thread interference.
 To prevent consistency problem.

Types of Synchronization
There are two types of synchronization
 Process Synchronization
 Thread Synchronization

Thread Synchronization
There are two types of thread synchronization mutual exclusive and inter-thread communication.
1.Mutual Exclusive
 Synchronized method.
 Synchronized block.
 static synchronization.
2.Cooperation (Inter-thread communication in java)
Synchronized method
 If you declare any method as synchronized, it is known as synchronized method.
 Synchronized method is used to lock an object for any shared resource.
 When a thread invokes a synchronized method, it automatically acquires the lock for that
object and releases it when the thread completes its task.
Example of synchronized method
package Thread;
public class SynThread
{
public static void main(String args[])
{
share s = new share();
MyThread m1 = new MyThread(s, "Thread1");
MyThread m2 = new MyThread(s, "Thread2");
MyThread m3 = new MyThread(s, "Thread3");
}
}
class MyThread extends Thread
{
share s;
MyThread(share s, String str)
{
super(str);
this.s = s;
start();
}
public void run()
{
s.doword(Thread.currentThread().getName());
}
}
class share
{
public synchronized void doword(String str)
{
for (int i = 0; i < 5; i++)
{
System.out.println("Started:" + str);
try
{
Thread.sleep(1000);
}
catch (Exception e)
{

}}}}
SYNCHRONIZED BLOCK
 Synchronized block can be used to perform synchronization on any specific resource of
the method.
 Suppose you have 50 lines of code in your method, but you want to synchronize only 5
lines, you can use synchronized block.
 If you put all the codes of the method in the synchronized block, it will work same as the
synchronized method.
Example of synchronized block
class Table{
void printTable(int n){
synchronized(this){//synchronized block
for(int i=1;i<=5;i++){
System.out.println(n*i);
try{
Thread.sleep(400);
}catch(Exception e){System.out.println(e);}
}
}
}//end of the method
}

class MyThread1 extends Thread{


Table t;
MyThread1(Table t){
this.t=t;
}
public void run(){
t.printTable(5);
}
}
class MyThread2 extends Thread{
Table t;
MyThread2(Table t){
this.t=t;
}
public void run(){
t.printTable(100);
}
}
public class TestSynchronizedBlock1{
public static void main(String args[]){
Table obj = new Table();//only one object
MyThread1 t1=new MyThread1(obj);
MyThread2 t2=new MyThread2(obj);
t1.start();
t2.start();
} }
STATIC SYNCHRONIZATION
If you make any static method as synchronized, the lock will be on the class not on
object.
Example of static synchronization
In this example we are applying synchronized keyword on the static method to perform
static synchronization.

class Table{
synchronized static void printTable(int n){
for(int i=1;i<=10;i++){
System.out.println(n*i);
try{
Thread.sleep(400);
}catch(Exception e){}
}
}
}
class MyThread1 extends Thread{
public void run(){
Table.printTable(1);
}
}
class MyThread2 extends Thread{
public void run(){
Table.printTable(10);
}
}
class MyThread3 extends Thread{
public void run(){
Table.printTable(100);
}
}
class MyThread4 extends Thread{
public void run(){
Table.printTable(1000);
}
}
public class TestSynchronization4{
public static void main(String t[]){
MyThread1 t1=new MyThread1();
MyThread2 t2=new MyThread2();
MyThread3 t3=new MyThread3();
MyThread4 t4=new MyThread4();
t1.start();
t2.start();
t3.start();
t4.start();
}
}

INTER-THREAD COMMUNICATION
Inter-thread communication or Co-operation is all about allowing synchronized threads to
communicate with each other.
Inter-thread communication is a mechanism in which a thread is paused running in its
critical section and another thread is allowed to enter (or lock) in the same critical section to be
executed.It is implemented by following methods of Object class:
 wait()
 notify()
 notifyAll()

wait()
tells calling thread to give up monitor and go to sleep until some other thread enters the
same monitor and call notify.
notify()
wakes up a thread that called wait() on same object.
notifyAll()
wakes up all the thread that called wait() on same object.

wait() sleep()

wait() method releases the lock sleep() method doesn't release the lock.

is the method of Object class is the method of Thread class

is the non-static method is the static method

should be notified by notify() or after the specified amount of time,


notifyAll() methods sleep is completed.
Example of inter thread communication in java
class Customer{
int amount=10000;
synchronized void withdraw(int amount){
System.out.println("going to withdraw...");
if(this.amount<amount){
System.out.println("Less balance; waiting for deposit...");
try{wait();}catch(Exception e){}
}
this.amount-=amount;
System.out.println("withdraw completed...");
}
synchronized void deposit(int amount){
System.out.println("going to deposit...");
this.amount+=amount;
System.out.println("deposit completed... ");
notify();
}
}
class Test{
public static void main(String args[]){
final Customer c=new Customer();
new Thread(){
public void run(){c.withdraw(15000);}
}.start();
new Thread(){
public void run(){c.deposit(10000);}
}.start();
}}

DAEMON THREAD IN JAVA


Daemon thread in java is a service provider thread that provides services to the user
thread. Its life depend on the mercy of user threads i.e. when all the user threads dies, JVM
terminates this thread automatically.
Example:
public class TestDaemonThread1 extends Thread{
public void run(){
if(Thread.currentThread().isDaemon()){//checking for daemon thread
System.out.println("daemon thread work");
}
else{
System.out.println("user thread work");
}
}
public static void main(String[] args){
TestDaemonThread1 t1=new TestDaemonThread1();//creating thread
TestDaemonThread1 t2=new TestDaemonThread1();
TestDaemonThread1 t3=new TestDaemonThread1();
t1.setDaemon(true);//now t1 is daemon thread
t1.start();//starting threads
t2.start();
t3.start();
}
}
Thread Group
Java provides a convenient way to group multiple threads in a single object. In such way,
we can suspend, resume or interrupt group of threads by a single method call.
Constructors of ThreadGroup class
There are only two constructors of ThreadGroup class.
1.ThreadGroup(String name)-creates a thread group with given name.
2.ThreadGroup(ThreadGroup parent, String name)-creates a thread group with
given parent group and name.
Important methods of ThreadGroup class
There are many methods in ThreadGroup class. A list of important methods are given
below.

1)int activeCount()-returns no. of threads running in current group.


2)int activeGroupCount()-returns a no. of active group in this thread group.
3)void destroy()-destroys this thread group and all its sub groups.
4)String getName()-returns the name of this group.
5)ThreadGroup getParent()-returns the parent of this group.
6)void interrupt()-interrupts all threads of this group.
7)void list()-prints information of this group to standard console.

Let's see a code to group multiple threads.

ThreadGroup tg1 = new ThreadGroup("Group A");


Thread t1 = new Thread(tg1,new MyRunnable(),"one");
Thread t2 = new Thread(tg1,new MyRunnable(),"two");
Thread t3 = new Thread(tg1,new MyRunnable(),"three");

Now all 3 threads belong to one group. Here, tg1 is the thread group name, MyRunnable
is the class that implements Runnable interface and "one", "two" and "three" are the thread
names.

Now we can interrupt all threads by a single line of code only.


Thread.currentThread().getThreadGroup().interrupt();

ThreadGroup Example
public class ThreadGroupDemo implements Runnable{
public void run() {
System.out.println(Thread.currentThread().getName());
}
public static void main(String[] args) {
ThreadGroupDemo runnable = new ThreadGroupDemo();
ThreadGroup tg1 = new ThreadGroup("Parent ThreadGroup");
Thread t1 = new Thread(tg1, runnable,"one");
t1.start();
Thread t2 = new Thread(tg1, runnable,"two");
t2.start();
Thread t3 = new Thread(tg1, runnable,"three");
t3.start();

System.out.println("Thread Group Name: "+tg1.getName());


tg1.list();

}
}
Output:
one
two
three
Thread Group Name: Parent ThreadGroup
java.lang.ThreadGroup[name=Parent ThreadGroup,maxpri=10]
Thread[one,5,Parent ThreadGroup]
Thread[two,5,Parent ThreadGroup]
Thread[three,5,Parent ThreadGroup]

Critical Factor in Thread – Deadlock


Deadlock in Java is a part of multithreading. Deadlock can occur in a situation when a thread is
waiting for an object lock, that is acquired by another thread and second thread is waiting for an
object lock that is acquired by first thread. Since, both threads are waiting for each other to
release the lock, the condition is called deadlock.

public class TestDeadlockExample1 {


public static void main(String[] args) {
final String resource1 = "ratan jaiswal";
final String resource2 = "vimal jaiswal";
// t1 tries to lock resource1 then resource2
Thread t1 = new Thread() {
public void run() {
synchronized (resource1) {
System.out.println("Thread 1: locked resource 1");

try { Thread.sleep(100);} catch (Exception e) {}


synchronized (resource2) {
System.out.println("Thread 1: locked resource 2");
}
}
}
};

// t2 tries to lock resource2 then resource1


Thread t2 = new Thread() {
public void run() {
synchronized (resource2) {
System.out.println("Thread 2: locked resource 2");

try { Thread.sleep(100);} catch (Exception e) {}

synchronized (resource1) {
System.out.println("Thread 2: locked resource 1");
}
}
}
};
t1.start();
t2.start();
}
}
OUTPUT:
Thread 1: locked resource 1
Thread 2: locked resource 2
How to Avoid Deadlock in Java?
Deadlocks cannot be completely resolved. But we can avoid them by following basic rules
mentioned below:
1. Avoid Nested Locks: We must avoid giving locks to multiple threads, this is the main
reason for a deadlock condition. It normally happens when you give locks to multiple
threads.
2. Avoid Unnecessary Locks: The locks should be given to the important threads. Giving
locks to the unnecessary threads that cause the deadlock condition.
3. Using Thread Join: A deadlock usually happens when one thread is waiting for the other
to finish. In this case, we can use join with a maximum time that a thread will take.

Introduction to Generics:
Generics means parameterized types. The idea is to allow type (Integer, String, … etc., and
user-defined types) to be a parameter to methods, classes, and interfaces. Using Generics, it is
possible to create classes that work with different data types. An entity such as class, interface, or
method that operates on a parameterized type is a generic entity.
The Object is the superclass of all other classes, and Object reference can refer to any
object. These features lack type safety. Generics add that type of safety feature. We will discuss
that type of safety feature in later examples.
Generics in Java are similar to templates in C++. For example, classes like HashSet,
ArrayList, HashMap, etc., use generics very well. There are some fundamental differences
between the two approaches to generic types.

Types of Java Generics


Generic Method: Generic Java method takes a parameter and returns some value after
performing a task. It is exactly like a normal function, however, a generic method has type
parameters that are cited by actual type. This allows the generic method to be used in a more
general way. The compiler takes care of the type of safety which enables programmers to code
easily since they do not have to perform long, individual type castings.
Generic Classes: A generic class is implemented exactly like a non-generic class. The
only difference is that it contains a type parameter section. There can be more than one type of
parameter, separated by a comma. The classes, which accept one or more parameters, ?are
known as parameterized classes or parameterized types.
Generic Class
Like C++, we use <> to specify parameter types in generic class creation. To create
objects of a generic class, we use the following syntax.
// To create an instance of generic class
BaseType <Type> obj = new BaseType <Type>()
Note: In Parameter type we can not use primitives like ‘int’,’char’ or ‘double’.

// Java program to show working of user defined


// Generic classes

// We use < > to specify Parameter type


class Test<T> {
// An object of type T is declared
T obj;
Test(T obj) { this.obj = obj; } // constructor
public T getObject() { return this.obj; }
}

// Driver class to test above


class Main {
public static void main(String[] args)
{
// instance of Integer type
Test<Integer> iObj = new Test<Integer>(15);
System.out.println(iObj.getObject());

// instance of String type


Test<String> sObj
= new Test<String>("WELCOME JAVA");
System.out.println(sObj.getObject());
}
}
Output:
15
WELCOME JAVA

We can also pass multiple Type parameters in Generic classes.


// Java program to show multiple
// type parameters in Java Generics

// We use < > to specify Parameter type


class Test<T, U>
{
T obj1; // An object of type T
U obj2; // An object of type U

// constructor
Test(T obj1, U obj2)
{
this.obj1 = obj1;
this.obj2 = obj2;
}

// To print objects of T and U


public void print()
{
System.out.println(obj1);
System.out.println(obj2);
}
}

// Driver class to test above


class Main
{
public static void main (String[] args)
{
Test <String, Integer> obj =
new Test<String, Integer>("GfG", 15);

obj.print();
}
}
Output:
GfG
15

Generic Functions:
We can also write generic functions that can be called with different types of arguments
based on the type of arguments passed to the generic method. The compiler handles each
method.
// Java program to show working of user defined
// Generic functions

class Test {
// A Generic method example
static <T> void genericDisplay(T element)
{
System.out.println(element.getClass().getName()
+ " = " + element);
}

// Driver method
public static void main(String[] args)
{
// Calling generic method with Integer argument
genericDisplay(11);

// Calling generic method with String argument


genericDisplay("WELCOME JAVA");

// Calling generic method with double argument


genericDisplay(1.0);
}
}

Output:
java.lang.Integer = 11
java.lang.String = WELCOME JAVA
java.lang.Double = 1.0

Generics Work Only with Reference Types:


When we declare an instance of a generic type, the type argument passed to the type parameter
must be a reference type. We cannot use primitive data types like int, char.
Test<int> obj = new Test<int>(20);
The above line results in a compile-time error that can be resolved using type wrappers to
encapsulate a primitive type.But primitive type arrays can be passed to the type parameter
because arrays are reference types.
ArrayList<int[]> a = new ArrayList<>();
Generic Types Differ Based on Their Type Arguments:
// Java program to show working
// of user-defined Generic classes

// We use < > to specify Parameter type


class Test<T> {
// An object of type T is declared
T obj;
Test(T obj) { this.obj = obj; } // constructor
public T getObject() { return this.obj; }
}

// Driver class to test above


class Main {
public static void main(String[] args)
{
// instance of Integer type
Test<Integer> iObj = new Test<Integer>(15);
System.out.println(iObj.getObject());

// instance of String type


Test<String> sObj
= new Test<String>("JAVA PROGRAMMING");
System.out.println(sObj.getObject());
iObj = sObj; // This results an error
}
}
Output:
error:
incompatible types:
Test cannot be converted to Test
Type Parameters in Java Generics
The type parameters naming conventions are important to learn generics thoroughly. The
common type parameters are as follows:
 T – Type
 E – Element
 K – Key
 N – Number
 V – Value
Advantages of Generics:
Programs that use Generics has got many benefits over non-generic code.
1. Code Reuse: We can write a method/class/interface once and use it for any type we want.
2. Type Safety: Generics make errors to appear compile time than at run time (It’s always better
to know problems in your code at compile time rather than making your code fail at run time).
Suppose you want to create an ArrayList that store name of students, and if by mistake the
programmer adds an integer object instead of a string, the compiler allows it. But, when we
retrieve this data from ArrayList, it causes problems at runtime.
// Java program to demonstrate that NOT using
// generics can cause run time exceptions

import java.util.*;

class Test
{
public static void main(String[] args)
{
// Creatinga an ArrayList without any type specified
ArrayList al = new ArrayList();

al.add("Welcome");
al.add("Java");
al.add(10); // Compiler allows this

String s1 = (String)al.get(0);
String s2 = (String)al.get(1);

// Causes Runtime Exception


String s3 = (String)al.get(2);
}
}
Output:
Exception in thread "main" java.lang.ClassCastException:
java.lang.Integer cannot be cast to java.lang.String
at Test.main(Test.java:19)

How do Generics Solve this Problem?


When defining ArrayList, we can specify that this list can take only String objects.
// Using Java Generics converts run time exceptions into
// compile time exception.
import java.util.*;

class Test
{
public static void main(String[] args)
{
// Creating a an ArrayList with String specified
ArrayList <String> al = new ArrayList<String> ();

al.add("Welcome");
al.add("java");

// Now Compiler doesn't allow this


al.add(10);

String s1 = (String)al.get(0);
String s2 = (String)al.get(1);
String s3 = (String)al.get(2);
}
}
Output:
15: error: no suitable method found for add(int)
al.add(10);
^
Implementing Generic Algorithms: By using generics, we can implement algorithms that work
on different types of objects, and at the same, they are type-safe too.
Generics are basically the errors appearing are compile-time than at run-time. there are certain
advantages of generics over non-generic are as follows:
1. Code Reuse: With help of Generics, one needs to write a method/class/interface only
once and use it for any type whereas, in non-generics, the code needs to be written again
and again whenever needed.
2. Type Safety: Generics make errors to appear compile time than at run time (It’s always
better to know problems in your code at compile time rather than making your code fail
at run time).
Example: To create an ArrayList that store name of students and if by mistake programmer adds
an integer object instead of string, the compiler allows it. But, when this data is retrieved from
ArrayList, it causes problems at runtime for Non-generic ArrayList
// Java program to Demonstrate that Not Using Generics
// Can cause Run Time Exceptions

// Importing all utility classes


import java.util.*;

// Main class
class GFG {

// Main driver method


public static void main(String[] args)
{
// Creating an ArrayList
// Declaring object without any type specified
ArrayList al = new ArrayList();
// Adding elements to the above object
// Custom input elements
al.add("Sachin");
al.add("Rahul");

// Compiler will allows this operation


al.add(10);

String s1 = (String)al.get(0);
String s2 = (String)al.get(1);

// Try block to check for exceptions


try {

// Causes Runtime Exception


String s3 = (String)al.get(2);
}

// Catch block to handle the exceptions


catch (Exception e) {

// Display the exception


System.out.println("Exception: " + e);
}
}
}

How generics solve this problem?


If this list was made Generic, then it would take only String objects and threw Compile Time
Error in any other case.
// Java Program to Illustrate Conversion of
// Runtime Exceptions into compile time errors
// Using generics

// Importing all utility classes


import java.util.*;

// Main class
class GFG {

// Main driver method


public static void main(String[] args)
{
// Creating an ArrayList
// Declaring object of string type
ArrayList<String> al = new ArrayList<String>();

// Adding elements to the ArrayList


// Custom input elements
al.add("Sachin");
al.add("Rahul");

// Now compiler does not allow this operation


al.add(10);

String s1 = al.get(0);
String s2 = al.get(1);
String s3 = al.get(2);
}
}

Output:
prog.java:24: error: incompatible types: int cannot be converted to String
al.add(10);
^
Note: Some messages have been simplified; recompile with -Xdiags:verbose to get full output
1 error
Now moving forward, Individual Type Casting is not needed.
If Generics is not needed, then, in the above example every time the data is to be retrieved from
ArrayList, it needs to be typecasted. Typecasting at every retrieval operation is a big headache.
This can be avoided if somehow it is already known that the list only holds string data.
// A Simple Java program to demonstrate that
// type casting is not needed in Generic

import java.util.*;

class Test {
public static void main(String[] args)
{
// Creating an ArrayList
// Declaring object of type String
ArrayList<String> al = new ArrayList<String>();

// Custom input elements


al.add("Sachin");
al.add("Rahul");

// Retrieval can be easily


// without the trouble of casting
String s1 = al.get(0);
String s2 = al.get(1);
// Print and display out the elements in objects
System.out.print(al);
}
}

Output:
[Sachin, Rahul]

Differentate:
Base Non-generic Collection Generic Collection

ArrayList list = new ArrayList<ReferenceType> list = new


Syntax ArrayList(); ArrayList<ReferenceType>();

Can hold any type of data. Can hold only the defined type of data.
Type-safety Hence not type-safe. Hence type safe.

Individual type casting needs


No typecasting is needed.
Typecasting to be done at every retrieval.

Compile-Time Checked for type safety at


Checked for type safety at Compile-time.
Checking runtime.

You might also like