0% found this document useful (0 votes)
35 views108 pages

Unit IV Final

This document discusses several topics related to Java multithreading and modules: - It covers multithreading fundamentals like thread life cycle, ways to create threads using Thread class and Runnable interface, thread priorities, and thread synchronization. - It also discusses annotations, lambda expressions, and Java modules including module basics, unnamed modules, and specific modules. - The last section covers module-based services and service providers.

Uploaded by

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

Unit IV Final

This document discusses several topics related to Java multithreading and modules: - It covers multithreading fundamentals like thread life cycle, ways to create threads using Thread class and Runnable interface, thread priorities, and thread synchronization. - It also discusses annotations, lambda expressions, and Java modules including module basics, unnamed modules, and specific modules. - The last section covers module-based services and service providers.

Uploaded by

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

Unit IV

Multithreading and Modules


Multithreading-Fundamentals, Thread Life Cycle, Ways of creating threads - Thread class and
Runnable interface, Thread priorities, Creating multiple threads, core methods of Thread class,
Thread Synchronization, inter thread communication.

Annotations- Annotation Basics, Specifying a Retention Policy, the Annotated Element Interface,
Using Default Values, Marker Annotations, Single – Member Annotations.

Lambda Expressions: Lambda Expression Fundamentals, Functional Interfaces, Block Lambda


Expressions, Passing Lambda Expressions as Arguments, Lambda Expressions and Exceptions,
Variable Capture, Method References,

Modules: Module Basics- module, exports, require, transitive, java.base and the Platform Modules,
Unnamed Module, Specific Module.

Service and Service Provider: Module-Based Service.

Multithreading
Fundamentals
 To redude idle time of processor and improves performance of the system is the main
purpose of “Multitasking”.
 Multithreading is the phenomenon of executing more than one thread(or) Operation
in the system, where the execution of these threads can be of two different types,
such as Concurrent and Parallel multithread executions.
 A Thread can be defined as a chunk or unit of process, that can be identified as either
a user-level thread or a Kernel-level thread. It is usually used for its essential
characteristics like it uses the system resources efficiently, high performance, greatly
responsive, and also its parallel execution ability.
 Multithreading in Java is a process of executing multiple threads simultaneously.
 A thread is a lightweight sub-process, the smallest unit of processing.
 Multiprocessing and multithreading, both are used to achieve multitasking.
 However, we use multithreading than multiprocessing because threads use a shared
memory area. They don't allocate separate memory area so saves memory, and
context-switching between the threads takes less time than process.
 Java Multithreading is mostly used in games, animation, etc.

Advantages of Java Multithreading

1) It doesn't block the user because threads are independent and you can perform multiple
operations at the same time.

2) You can perform many operations together, so it saves time.

3) Threads are independent, so it doesn't affect other threads if an exception occurs in a


single thread.

Multitasking

 Whether it is process-based or thread-based,the main objective of multitasking is to


reduce the response time of the system and to improve performance.

Multitasking is a process of executing multiple tasks simultaneously. We use multitasking to


utilize the CPU. Multitasking can be achieved in two ways:

o Process-based Multitasking (Multiprocessing)


o Thread-based Multitasking (Multithreading)

1) Process-based Multitasking (Multiprocessing)


o Excecuting several tasks simultaneously where each task is separate independent
program(process) is called “process base multitasking”.
o Ex:While typing a java program in the editor,we can listen audio songs from the same
system at the same time we can download a file from internet.All these tasks will be
executed simultaneously under independent of each other, Hence it is process based
multitasking.
o It is best suitable at os level.
o Each process has an address in memory. In other words, each process allocates a
separate memory area.
o A process is heavyweight.
o Cost of communication between the process is high.
o Switching from one process to another requires some time for saving and
loading registers, memory maps, updating lists, etc.

2) Thread-based Multitasking (Multithreading)


o Excecuting 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 a “Thread”.
o It is best suitable at programatic level .
o Threads share the same address space.
o A thread is lightweight.
o Cost of communication between the thread is low.

Understanding Multithreading
There are two terms that need to be understood :

1. Thread: Thread is the independent or basic unit of a process or program.


2. Process: A program which is being executed is called a process, multiple threads
exist in a process.

The execution in this is both concurrent and parallel.

 Concurrent Execution: If the processor can switch execution resources between


threads in a multithreaded process on a single processor, then it is said to be a
concurrent execution.
 Parallel Execution: When each thread in the process can run on a separate processor
at the same time in the same multithreaded process, then it is said to be a parallel
execution.

Life Cycle of a Thread


A thread goes through various stages in its life cycle. For example, a thread is born, started,
runs, and then dies. The following diagram shows the complete life cycle of a thread.

In Java, a thread always exists in any one of the following states. These states are:

1. New
2. Active
3. Blocked / Waiting
4. Timed Waiting
5. Terminated
Follow ing are the stages of the life cycle –

New: Whenever a new thread is created, it is always in the new state. For a thread in
the new state, the code has not been run yet and thus has not begun its execution.

Active: When a thread invokes the start() method, it moves from the new state to the
active state. The active state contains two states within it: one is runnable, and the
other is running.

o Runnable: A thread, that is ready to run is then moved to the runnable state.
In the runnable state, the thread may be running or may be ready to run at
any given instant of time. It is the duty of the thread scheduler to provide the
thread time to run, i.e., moving the thread the running state.
A program implementing multithreading acquires a fixed slice of time to each
individual thread. Each and every thread runs for a short span of time and
when that allocated time slice is over, the thread voluntarily gives up the CPU
to the other thread, so that the other threads can also run for their slice of
time. Whenever such a scenario occurs, all those threads that are willing to
run, waiting for their turn to run, lie in the runnable state. In the runnable
state, there is a queue where the threads lie.
o Running: When the thread gets the CPU, it moves from the runnable to the
running state. Generally, the most common change in the state of a thread is
from runnable to running and again back to runnable.

Blocked or Waiting: Whenever a thread is inactive for a span of time (not


permanently) then, either the thread is in the blocked state or is in the waiting state.

For example, a thread (let's say its name is A) may want to print some data from the
printer. However, at the same time, the other thread (let's say its name is B) is using
the printer to print some data. Therefore, thread A has to wait for thread B to use the
printer. Thus, thread A is in the blocked state. A thread in the blocked state is unable
to perform any execution and thus never consume any cycle of the Central
Processing Unit (CPU). Hence, we can say that thread A remains idle until the thread
scheduler reactivates thread A, which is in the waiting or blocked state.

When the main thread invokes the join() method then, it is said that the main thread
is in the waiting state. The main thread then waits for the child threads to complete
their tasks. When the child threads complete their job, a notification is sent to the
main thread, which again moves the thread from waiting to the active state.

If there are a lot of threads in the waiting or blocked state, then it is the duty of the
thread scheduler to determine which thread to choose and which one to reject, and
the chosen thread is then given the opportunity to run.

Timed Waiting: Sometimes, waiting for leads to starvation. For example, a thread (its
name is A) has entered the critical section of a code and is not willing to leave that
critical section. In such a scenario, another thread (its name is B) has to wait forever,
which leads to starvation. To avoid such scenario, a timed waiting state is given to
thread B. Thus, thread lies in the waiting state for a specific span of time, and not
forever. A real example of timed waiting is when we invoke the sleep() method on a
specific thread. The sleep() method puts the thread in the timed wait state. After the
time runs out, the thread wakes up and start its execution from when it has left
earlier.

Terminated: A thread reaches the termination state because of the following


reasons:

o When a thread has finished its job, then it exists or terminates normally.
o Abnormal termination: It occurs when some unusual events such as an
unhandled exception or segmentation fault.
A terminated thread means the thread is no more in the system. In other words, the
thread is dead, and there is no way one can respawn (active after kill) the dead
thread.

Ways of creating thraeads


Threads can be created by using two mechanisms :
1. Extending the Thread class
2. Implementing the Runnable Interface
1.Thread creation by extending the Thread class

We create a class that extends the java.lang.Thread class.


 This class overrides the run() method available in the Thread class.
 A thread begins its life inside run() method.
 Create an object of our new class and call start() method to start the execution of a
thread. Start() invokes the run() method on the Thread object.

Example:

// Java code for thread creation by extending the Thread class

class MultithreadingDemo extends Thread {

public void run()

try {

// Displaying the thread that is running

System.out.println("Thread " + Thread.currentThread().getId() + " is running");

catch (Exception e) {
// Throwing an exception

System.out.println("Exception is caught");

// Main Class

public class Multithread {

public static void main(String[] args)

int n = 8; // Number of threads

for (int i = 0; i < n; i++) {

MultithreadingDemo object = new MultithreadingDemo();

object.start();

Output
Thread 15 is running
Thread 14 is running
Thread 16 is running
Thread 12 is running
Thread 11 is running
Thread 13 is running
Thread 18 is running
Thread 17 is running

2.Thread creation by implementing the Runnable Interface


We create a new class which implements java.lang.Runnable interface and override run()
method. Then we instantiate a Thread object and call start() method on this object.

// Java code for thread creation by implementing the Runnable Interface

class MultithreadingDemo implements Runnable {

public void run()

try {

// Displaying the thread that is running

System.out.println( "Thread " + Thread.currentThread().getId() + " is running");

catch (Exception e) {

// Throwing an exception

System.out.println("Exception is caught");

// Main Class

class Multithread {
public static void main(String[] args)

int n = 8; // Number of threads

for (int i = 0; i < n; i++) {

Thread object = new Thread(new MultithreadingDemo());

object.start();

Output
Thread 13 is running
Thread 11 is running
Thread 12 is running
Thread 15 is running
Thread 14 is running
Thread 18 is running
Thread 17 is running
Thread 16 is running
Thread Class vs Runnable Interface
1. If we extend the Thread class, our class cannot extend any other class because Java
doesn’t support multiple inheritance. But, if we implement the Runnable interface, our
class can still extend other base classes.
2. We can achieve basic functionality of a thread by extending Thread class because it
provides some inbuilt methods like yield(), interrupt() etc. that are not available in
Runnable interface.
3. Using runnable will give you an object that can be shared amongst multiple threads.

Core methods of Thread class


Java Thread Methods
S.N Modifier Method Description
. and Type

1) void start() It is used to start the execution of the thread.

2) void run() It is used to do an action for a thread.

3) static void sleep() It sleeps a thread for the specified amount of time.

4) static Thread currentThread() It returns a reference to the currently executing


thread object.

5) void join() It waits for a thread to die.

6) int getPriority() It returns the priority of the thread.

7) void setPriority() It changes the priority of the thread.

8) String getName() It returns the name of the thread.

9) void setName() It changes the name of the thread.

10) long getId() It returns the id of the thread.

11) boolean isAlive() It tests if the thread is alive.

12) static void yield() It causes the currently executing thread object to
pause and allow other threads to execute
temporarily.

13) void suspend() It is used to suspend the thread.

14) void resume() It is used to resume the suspended thread.


15) void stop() It is used to stop the thread.

16) void destroy() It is used to destroy the thread group and all of its
subgroups.

17) boolean isDaemon() It tests if the thread is a daemon thread.

18) void setDaemon() It marks the thread as daemon or user thread.

19) void interrupt() It interrupts the thread.

20) boolean isinterrupted() It tests whether the thread has been interrupted.

21) static interrupted() It tests whether the current thread has been
boolean interrupted.

22) static int activeCount() It returns the number of active threads in the
current thread's thread group.

23) void checkAccess() It determines if the currently running thread has


permission to modify the thread.

24) static holdLock() It returns true if and only if the current thread holds
boolean the monitor lock on the specified object.

25) static void dumpStack() It is used to print a stack trace of the current thread
to the standard error stream.

26) StackTraceEl getStackTrace() It returns an array of stack trace elements


ement[] representing the stack dump of the thread.

27) static int enumerate() It is used to copy every active thread's thread group
and its subgroup into the specified array.

28) Thread.State getState() It is used to return the state of the thread.


29) ThreadGroup getThreadGroup It is used to return the thread group to which this
() thread belongs

30) String toString() It is used to return a string representation of this


thread, including the thread's name, priority, and
thread group.

31) void notify() It is used to give the notification for only one
thread which is waiting for a particular object.

32) void notifyAll() It is used to give the notification to all waiting


threads of a particular object.

33) void setContextClass It sets the context ClassLoader for the Thread.
Loader()

34) ClassLoader getContextClass It returns the context ClassLoader for the thread.
Loader()

35) static getDefaultUnca It returns the default handler invoked when a thread
Thread.Unca ughtExceptionH abruptly terminates due to an uncaught exception.
ughtExceptio andler()
nHandler

36) static void setDefaultUnca It sets the default handler invoked when a thread
ughtExceptionH abruptly terminates due to an uncaught exception.
andler()

Java Thread Priority

In a java programming language, every thread has a property called priority. Most of the
scheduling algorithms use the thread priority to schedule the execution sequence. In java, the
thread priority range from 1 to 10. Priority 1 is considered as the lowest priority, and priority
10 is considered as the highest priority. The thread with more priority allocates the processor
first.
The java programming language Thread class provides two methods setPriority(int),
and getPriority( ) to handle thread priorities.
The Thread class also contains three constants that are used to set the thread priority, and they
are listed below.

 MAX_PRIORITY - It has the value 10 and indicates highest priority.


 NORM_PRIORITY - It has the value 5 and indicates normal priority.
 MIN_PRIORITY - It has the value 1 and indicates lowest priority.
 The default priority of any thread is 5 (i.e. NORM_PRIORITY).

setPriority( ) method
 The setPriority( ) method of Thread class used to set the priority of a thread. It takes
an integer range from 1 to 10 as an argument and returns nothing (void).
 The regular use of the setPriority( ) method is as follows.
Example

threadObject.setPriority(4);
or
threadObject.setPriority(MAX_PRIORITY);

getPriority( ) method
The getPriority( ) method of Thread class used to access the priority of a thread. It does not
takes anyargument and returns name of the thread as String.
The regular use of the getPriority( ) method is as follows.
Example

Int value = threadObject.getPriority();

Look at the following example program.


Example

class SampleThread extends Thread{


public void run() {
System.out.println("Inside SampleThread");
System.out.println("Current Thread: " +
Thread.currentThread().getName());
}
}

public class My_Thread_Test {

public static void main(String[] args) {


SampleThread threadObject1 = new SampleThread();
SampleThread threadObject2 = new SampleThread();
threadObject1.setName("first");
threadObject2.setName("second");

threadObject1.setPriority(4);
threadObject2.setPriority(Thread.MAX_PRIORITY);

threadObject1.start();
threadObject2.start();

}
}

Note:In java, it is not guaranteed that threads execute according to their priority because it
depends on JVM specification that which scheduling it chooses.

Synchronizing threads in Java


 When we start two or more threads within a program, there may be a
situation when multiple threads try to access the same resource and
finally they can produce unforeseen result due to concurrency issues.
 For example, if multiple threads try to write within a same file then they
may corrupt the data because one of the threads can override data or
while one thread is opening the same file at the same time another thread
might be closing the same file.
 So there is a need to synchronize the action of multiple threads and make
sure that only one thread can access the resource at a given point in time.
 Java programming language provides a very handy way of creating
threads and synchronizing their task by using “synchronized” blocks.
You keep shared resources within this block. Following is the general
form of the synchronized statement −
Syntax
synchronized(objectidentifier)
{
// Access shared variables and other shared resources
}

Note:Here, the objectidentifier is a reference to an object whose lock


associates with the monitor that the synchronized statement represents.

Example: Threads Execution without Synchronization


class PrintDemo
{
public void printCount()
{
try
{
for(int i = 5; i > 0; i--)
{
System.out.println("Counter --- " + i );
}
} catch (Exception e) {
System.out.println("Thread interrupted.");
}
}
}

class ThreadDemo extends Thread


{
private Thread t;
private String threadName;
PrintDemo PD;

ThreadDemo( String name, PrintDemo pd)


{
threadName = name;
PD = pd;
}

public void run()


{
PD.printCount();
System.out.println("Thread " + threadName + " exiting.");
}

public void start ()


{
System.out.println("Starting " + threadName );
if (t == null)
{
t = new Thread (this, threadName);
t.start ();
}
}
}

public class TestThread


{
public static void main(String args[])
{

PrintDemo PD = new PrintDemo();

ThreadDemo T1 = new ThreadDemo( "Thread - 1 ", PD );


ThreadDemo T2 = new ThreadDemo( "Thread - 2 ", PD );

T1.start();
T2.start();

}
}
This produces a different result every time you run this program −
Output
Starting Thread - 1
Starting Thread - 2
Counter --- 5
Counter --- 4
Counter --- 3
Counter --- 5
Counter --- 2
Counter --- 1
Counter --- 4
Thread Thread - 1 exiting.
Counter --- 3
Counter --- 2
Counter --- 1
Thread Thread - 2 exiting.

Multithreading Example with Synchronization


Here is the same example which prints counter value in sequence and every
time we run it, it produces the same result.

Example
class PrintDemo
{
public void printCount()
{
try
{
for(int i = 5; i > 0; i--)
{
System.out.println("Counter --- " + i );
}
} catch (Exception e) {
System.out.println("Thread interrupted.");
}
}
}

class ThreadDemo extends Thread


{
private Thread t;
private String threadName;
PrintDemo PD;

ThreadDemo( String name, PrintDemo pd)


{
threadName = name;
PD = pd;
}

public void run()


{
synchronized(PD)
{
PD.printCount();
}
System.out.println("Thread " + threadName + " exiting.");
}

public void start ()


{
System.out.println("Starting " + threadName );
if (t == null) {
t = new Thread (this, threadName);
t.start ();
}
}
}

public class TestThread {

public static void main(String args[]) {


PrintDemo PD = new PrintDemo();

ThreadDemo T1 = new ThreadDemo( "Thread - 1 ", PD );


ThreadDemo T2 = new ThreadDemo( "Thread - 2 ", PD );

T1.start();
T2.start();
T1.run();
T2.run();
}
}
This produces the same result every time you run this program −
Output
Starting Thread - 1
Starting Thread - 2
Counter --- 5
Counter --- 4
Counter --- 3
Counter --- 2
Counter --- 1
Thread Thread - 1 exiting.
Counter --- 5
Counter --- 4
Counter --- 3
Counter --- 2
Counter --- 1
Thread Thread - 2 exiting.

Inter-thread communication in Java


o Inter-thread communication or Co-operation is all about allowing
synchronized threads to communicate with each other.
o Cooperation (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(It’s a location where
the thread is executing its process) to be executed.
o It is implemented by following methods of Object class:

o wait()
o notify()
o notifyAll()

1) wait() method

o Causes current thread to release the lock and wait until either
another thread invokes the notify() method or the notifyAll()
method for this object, or a specified amount of time has elapsed.
o The current thread must own this object's monitor, so it must be
called from the synchronized method only otherwise it will throw
exception.

Method Description

public final void wait()throws InterruptedException waits until object is notified.

public final void wait(long timeout)throws waits for the specified amount of
InterruptedException time.
2) notify() method

Wakes up a single thread that is waiting on this object's monitor. If any threads
are waiting on this object, one of them is chosen to be awakened. The choice is
arbitrary and occurs at the discretion of the implementation. Syntax:

public final void notify()

3) notifyAll() method

Wakes up all threads that are waiting on this object's monitor. Syntax:

public final void notifyAll()

Understanding the process of inter-thread communication

The point to point explanation of the above diagram is as follows:

1. Threads enter to acquire lock.


2. Lock is acquired by on thread.
3. Now thread goes to waiting state if you call wait() method on the object.
Otherwise it releases the lock and exits.
4. If you call notify() or notifyAll() method, thread moves to the notified
state (runnable state).
5. Now thread is available to acquire lock.
6. After completion of the task, thread releases tt444tche lock and exits the
monitor state of the object.
Example of inter thread communication in java
Let's see the simple example of inter thread communication.

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[])
{
Customer c=new Customer();
new Thread()
{
public void run()
{
c.withdraw(15000);
}
}.start();
new Thread()
{
public void run()
{
c.deposit(10000);
}
}.start();

}}

Output: going to withdraw...

Less balance; waiting for deposit...

going to deposit...

deposit completed...

withdraw completed

Difference between Multiprocessing and Multithreading:


S.NO MULTIPROCESSING MULTITHREADING

While In Multithreading, many threads are

In Multiprocessing, CPUs are added for created of a single process for increasing

1. increasing computing power. computing power.

In Multiprocessing, Many processes are While in multithreading, many threads of a

2. executed simultaneously. process are executed simultaneously.

Multiprocessing are classified While Multithreading is not classified in any

3. into Symmetric and Asymmetric. categories.

In Multiprocessing, Process creation is a While in Multithreading, process creation is

4. time-consuming process. according to economical.

5. In Multiprocessing, every process owned While in Multithreading, a common address


a separate address space. space is shared by all the threads.
Annotations in Java

Introduction:

Annotation is a Java Feature provided by JDK 5.0 version,it can be used to


represent metadata in Java applications.

In Java applications, if we provide metadata by using comments then "Lexical


Analyzer" will remove comments metadata from Java program as part of
Compilation.

As per the application requirement, if we want to bring metadata upto .java file,
upto .class file and upto RUNTIME of our application then we have to use
"Annotations".

In Java applications, if we provide metadata by using XML document+ts then


we are able to get the following problems.1.Every time we have to check
whether XML documents are located properly or not.2.Every time we have to
check whether XML documents are formatted properly or not. So, to overcome
these problems we can use “Annotations”.

Annotations in Java provide additional information to the compiler and JVM.


An annotation is a tag representing metadata about classes, interfaces,
variables, methods, or fields. Annotations do not impact the execution of the
code that they annotate. Some of the characteristics of annotations are:

 Begin with ‘@’

 Do not alter the execution of the program

 Provide supplemental information and help to link metadata with


elements of a program such as classes, variables, constructs, methods,
etc.

 Are different from comments since they can affect how the program is
treated by the compiler.
Hierarchy of Annotations in Java

Types of Annotation

There are three types of annotations.

1. Marker Annotation
2. Single-Value Annotation
3. Multi-Value Annotation

How to create basic Custom annotation?


There are 2 types of syntaxes for annotations
1.Declaration Syntax
2.Utilization Syntax.
1.Delaration Syntax:
(i) Standard Annotations: For standard annotations declaration syntax
given by developers.
(ii) Custom Annotations
Syntax:

@interface Annotation_Name
{

//Set of members as methods

Example:

@interface Course

String cid() default "C-111";

String cname() default "C Programming";

int ccost() default 10000;

2.Utilization Syntax:

We can utilize both standard and custom annotations as

Syntax:

@AnnotationName(member1=value1,member2=value2,…..,member=valuen)

Description:
 Annotations are created by using @ sign, followed by the keyword interface, and
followed by annotation name as shown in the above example.
 Members can be decleared as shown in the example, it looks like methods. The
example defines two members called name and desc. We should not provide
implementation for these members.
 All annotations extends java.lang.annotation.Annotation interface. Annotations
cannot include any extends caluse.
 Below example shows how to use this annotation to method.

Example:
//Declaration of Annotation
public @interface MySampleAnn {
String name();
String desc();
}

class MyAnnTest{

//Utilization of Annotation
@MySampleAnn(name = "test1", desc = "testing annotations")
public void myTestMethod(){
//method implementation
}
}

What is Retention policy in java annotations?

Description:
 A retention policy determines to specify how long annotations ate to be
retained.
 Java defined 3 types of retention policies through
java.lang.annotation.RetentionPolicy enumeration.

They are

(i) SOURCE
(ii) CLASS
(iii) RUNTIME.
 Annotation with retention policy SOURCE will be retained only with source
code, and discarded during compile time.
 Annotation with retention policy CLASS will be retained till compiling the
code, and discarded during runtime.
 Annotation with retention policy RUNTIME will be available to the JVM
through runtime.
 The retention policy will be specified by using java built-in annotation
@Retention, and we have to pass the retention policy type.
 The default retention policy type is CLASS.

Example:
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Retention(RetentionPolicy.RUNTIME)
public @interface MySampleAnn {

String name();
String desc();
}

class MyAnnTest{

@MySampleAnn(name = "test1", desc = "testing annotations")


public void myTestMethod(){
//method implementation
}
}

How to get Annotations at Runtime using java reflection?

Description:
 In case if any annotation is specifies its retention policy as RUNTIME, then
those annotations can be queried at runtime by any java program.
 Below example code shows how to query an annotation values during runtime
using java reflection.

Note: Reflection is an API that is used to examine or modify the behavior of


methods, classes, and interfaces at runtime. The required classes for reflection
are provided under java.lang.reflect package

Example:
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Method;

@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation{

String key();
String value();
}

public class MyAnnotationTest {

@MyAnnotation(key="site", value="java2novice.com")
public void myAnnotationTestMethod(){

try {
Class<? extends MyAnnotationTest> cls = this.getClass();
Method mth = cls.getMethod("myAnnotationTestMethod");
MyAnnotation myAnno = mth.getAnnotation(MyAnnotation.class);
System.out.println("key: "+myAnno.key());
System.out.println("value: "+myAnno.value());
} catch (SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NoSuchMethodException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

public static void main(String a[]){

MyAnnotationTest mat = new MyAnnotationTest();


mat.myAnnotationTestMethod();
}
}
How to get all annotations from a class?

Description:
 In case if any annotation is specifies its retention policy as RUNTIME, then
those annotations can be queried at runtime by any java program.
 Below example code shows how to query all given annotations at runtime.

Example:

import java.lang.annotation.Annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Method;

@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnoOne{

String key();
String value();
}

@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnoTwo{

int count();
}

public class MyMultipleAnns {

@MyAnnoTwo(count=20)
@MyAnnoOne(key="site", value="java2novice.com")
public void myAnnotationTestMethod(){

try {
Class<? extends MyMultipleAnns> cls = this.getClass();
Method mth = cls.getMethod("myAnnotationTestMethod");
Annotation[] anns = mth.getAnnotations();
for(Annotation an:anns){
System.out.println(an);
}
} catch (SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NoSuchMethodException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

public static void main(String a[]){

MyMultipleAnns mat = new MyMultipleAnns();


mat.myAnnotationTestMethod();
}
}

How to assign default values to custom annotations?

Description:
 We can assign default values to annotation members. These values will
be return in case if you didn't provide any values to the annotation
members.
 We can assign default values by using default clause during members
declarations. Make sure that the default values are same type as members.
 Below example shows how to assign default values to annotation
members.

Example:
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Method;

@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnot{

String key() default "site";


String value() default "java2novice.com";
}

public class MyAnnotDefaultValues {

@MyAnnot()
public void myAnnotationTestMethod(){

try {
Class<? extends MyAnnotDefaultValues> cls = this.getClass();
Method mth = cls.getMethod("myAnnotationTestMethod");
MyAnnot myAnno = mth.getAnnotation(MyAnnot.class);
System.out.println("key: "+myAnno.key());
System.out.println("value: "+myAnno.value());
} catch (SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NoSuchMethodException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

public static void main(String a[]){

MyAnnotDefaultValues mat = new MyAnnotDefaultValues();


mat.myAnnotationTestMethod();
}
}

@Target Annotations
 Annotations in java are used to associate metadata to program elements
like classes, methods, instance variables, etc.
 There are mainly three types of annotations in java: Marker Annotation
(without any methods), Single-Valued Annotation (with a single
method), and Multi-Valued Annotation (with more than one method).
 @Target annotation is a meta-annotation, i.e., it can only be used to
annotate other annotations.
 It takes ElementType enumeration as its only argument. ElementType
enumeration is a constant which specifies the type of the program
element declaration (class, interface, constructor, etc.) to which the
annotation can be applied.
 If we apply the @Target annotation with some ElementType as an
annotation to a custom annotation name CustomAnnotation, then
@CustomAnnotation can only be used to annotate those specific
elements types otherwise we will get an error.
The table below shows the element types, and the element declaration that can
be annotated are mentioned below as follows:

Element Type Element to be Annotated

Type Class, interface or enumeration

Field Field

Method Method

Constructor Constructor

Local_Variable Local variable

Annotation_Type Annotation Type

Package PACKAGE
Element Type Element to be Annotated

Type_Parameter Type Parameter

Parameter Formal Parameter

In order to use @Target annotation follow the below syntax as follows:


Syntax:
@Target(ElementType.TYPE)
@interface CustomAnnotation {}
 In the code sample mentioned above, we have created a custom
annotation annotated using @Target annotation.
 Since we have used ElementType as TYPE, therefore, the custom
annotation can only annotate a class, enumeration, or interface.
 We can also create a custom annotation that can be applied to multiple
elements by giving a braces-delimited list of element types as an
argument to @Target annotation as shown below.
Syntax:
@Target({ElementType.METHOD, ElementType.PACKAGE})
@interface CustomAnnotation {}
 We have created two custom annotations: one which can be applied to a
single element type and one which can be applied to multiple element
types.
 Then we apply these annotations to our class and method, and finally,
we print these annotations by fetching them.

Example:
/* Java program to Illustrate Targeted Annotations Importing required
classes from java.lang package */
import java.lang.annotation.*;

// Creating a custom annotation with target as TYPE which means it can


//annotate a class, enumeration, or interface
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
// Class Annotation
@interface ClassAnnotation
{
String value() default "Can annotate a class";
}

// Creating custom annotation with target as multiple Element types as


//parameters which means it can annotate various Elements
@Target({ ElementType.METHOD, ElementType.TYPE,
ElementType.ANNOTATION_TYPE,
ElementType.CONSTRUCTOR })
@Retention(RetentionPolicy.RUNTIME)
@interface MultipleElementTypeAnnotation
{

String value() default "Can annotate a class, method, "


+ "annotation, or constructor";
}

// Class to demonstrate the use of custom created annotations


@ClassAnnotation

// Main class TargetAnnotationDemo


public class GFG {

@MultipleElementTypeAnnotation public void myMethod() {}

// Main drive method


public static void main(String[] args) throws Exception
{
GFG obj = new GFG();

// Accessing the annotations used to annotate the class and storing them
//in an array of Annotation type since only one annotation is used to
// annotate our class therefore we print a[0]
Annotation a[] = obj.getClass().getAnnotations();

System.out.println(a[0]);

// Accessing the annotations used to annotate the method and storing


// them in an array of Annotation type
Class<?> className = Class.forName("GFG");
Annotation b[] = className.getMethod("myMethod")
.getAnnotations();

System.out.println(b[0]);
}
}

Example1: Java Program to describe utilizing the annotation at class level


Course.java
//Declaring the Annotation
import java.lang.annotation.*;
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface Course
{
String cid() default "C-111";
String cname() default "C Programming";
int ccost() default 10000;
}

Student.java
//Utilizing the Annotation at Class level
@Course(cid="C-333",cname="Java",ccost=30000)
class Student
{
String sid;
String sname;
String saddr;
public Student(String sid,String sname,String saddr)
{
this.sid=sid;
this.sname=sname;
this.saddr=saddr;
}
// @Course()
public void getStudentDetails()
{
System.out.println("*****Student Course Details are*****");
System.out.println("-----------------------------");
System.out.println("Student id: "+sid);
System.out.println("Student name: "+sname);
System.out.println("Student Address: "+saddr);
}
}

UserDefAnnotation.java

import java.lang.annotation.*;
import java.lang.reflect.*;
class UserDefAnnotation
{
public static void main(String args[])throws Exception
{
Student std=new Student("S-111","Sreenidhi","Hyd");
std.getStudentDetails();
//Accessing data of Annotation
Class c=std.getClass();
//Method m=c.getMethod("getStudentDetails");
Annotation ann=c.getAnnotation(Course.class);
Course crs=(Course)ann;
System.out.println("Course id: "+crs.cid());
System.out.println("Course name: "+crs.cname());
System.out.println("Course Cost: "+crs.ccost());

}
}

Example2: Java Program to describe utilizing the annotation at Method


level
Course.java
//Declaring the Annotation
import java.lang.annotation.*;
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface Course
{
String cid() default "C-111";
String cname() default "C Programming";
int ccost() default 10000;
}
Student.java
//Utilizing the Annotation at Class level
class Student
{
String sid;
String sname;
String saddr;
public Student(String sid,String sname,String saddr)
{
this.sid=sid;
this.sname=sname;
this.saddr=saddr;
}
@Course(cid="C-333",cname="Java",ccost=30000)
public void getStudentDetails()
{
System.out.println("*****Student Course Details are*****");
System.out.println("-----------------------------");
System.out.println("Student id: "+sid);
System.out.println("Student name: "+sname);
System.out.println("Student Address: "+saddr);
}
}
UserDefAnnotation.java

import java.lang.annotation.*;
import java.lang.reflect.*;
class UserDefAnnotation
{
public static void main(String args[])throws Exception
{
Student std=new Student("S-111","Sreenidhi","Hyd");
std.getStudentDetails();
//Accessing data from Annotation
Class c=std.getClass();
Method m=c.getMethod("getStudentDetails");
Annotation ann=m.getAnnotation(Course.class);
Course crs=(Course)ann;
System.out.println("Course id: "+crs.cid());
System.out.println("Course name: "+crs.cname());
System.out.println("Course Cost: "+crs.ccost());

}
}
Output:
AnnotatedElement Interface
The getAnnotations() method of java.lang.AnnotatedElement class is used
to get the annotations present in the class implementing this interface. The
method returns an array of annotations present.
Syntax:
public Annotation[] getAnnotations()
Parameter: This method does not accepts any parameter.
Return Value: This method returns an array of annotations present.

Example:
// Java program to demonstrate getAnnotations() method

import java.lang.reflect.*;
import java.util.*;
import java.lang.annotation.*;

// create a custom Annotation


@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@interface Annotation {

// This annotation has two attributes.


public String key();
public String value();
}

// call Annotation for method and pass values for annotation


@Deprecated
@Annotation(key = "CVR", value = "CVR College for Engineering")
public class Test {

public static void main(String[] args)throws ClassNotFoundException


{

// returns the Class object for this myAnnotatedElement


AnnotatedElement myAnnotatedElement1= Test.class;
AnnotatedElement myAnnotatedElement2= Annotation.class;

// Get the annotation using getAnnotations() method


System.out.println("Annotations of Class Element: "
+ Arrays.toString(myAnnotatedElement1.getAnnotations()));
System.out.println("Annotations of Custom Annotation Element: "
+ Arrays.toString(myAnnotatedElement2.getAnnotations()));
}
}
Output:
Types of Annotations

Predefined/ Standard Annotations

 As indicated in the hierarchy of annotations above, Java provides built-in


or standard annotations.

 @Deprecated, @Override, and @SuppressWarnings are available in


java.lang &

 @Retention, @Documented, @Target, and @Inherited are imported from


java.lang.annotation package.

Annotation 1: @Deprecated

 The @Deprecated annotation is used to indicate that the class, field, or


method marked is no longer in use and has been replaced by a newer
form.

 Whenever a class, field, or method marked with the @Deprecated


annotation is used, the compiler gives a warning message that a
deprecated class, field, or method is used.

 When an element has been deprecated, the Javadoc tag @deprecated tag
must be used. There is a difference between the @deprecated tag and
@Deprecated annotation.

 While the @deprecated tag is used for documentation, the @Deprecated


annotation is used for runtime reflection.

Example

public class DeprecatedDemo

@Deprecated
public void Display()

System.out.println("Deprecateddemo display()");

public static void main(String args[])

DeprecatedDemo d1 = new DeprecatedDemo();

d1.Display();

Output

Deprecateddemo display()

Annotation 2: @Override

 @Override is a marker annotation that can only be used on methods.

 A method that is annotated with @Override must override a method from


the superclass.

 A compile-time error occurs if the method does not override the method
from the superclass.
 This ensures that the superclass method is overridden and not overloaded.
The code becomes more readable and maintenance issues can be
avoided.

Example

// Java Program to Illustrate Override Annotation

// Class 1

class ParentClass

public void Display()

System.out.println("Parent Class Display() Method");

public static void main(String args[])

ParentClass t1 = new ChildClass();

t1.Display();

// Class 2
// Extending above class

class ChildClass extends ParentClass

@Override

public void Display()

System.out.println("Child Class Display() Method");

Output

Child Class Display() Method

Annotation 3: @SuppressWarnings

 @SuppressWarnings is used to inform the compiler to suppress the


specified compiler warnings.

 This is done by specifying the warnings to be suppressed by specific


names. It can be applied to any type of declaration.

 There are two categories under which Java groups warnings - deprecated
and unchecked. When a legacy code interfaces with a code that uses
generics, an unchecked warning is generated.
Example

// Java Program to illustrate SuppressWarnings Annotation Class 1

class DeprecatedDemo

@Deprecated

public void Display()

System.out.println("Deprecateddemo display()");

// Class 2

public class SuppressWarningDemo

// If we comment below annotation, program generates

// warning

@SuppressWarnings({"checked", "deprecation"})

public static void main(String args[])

{
DeprecatedDemo d1 = new DeprecatedDemo();

d1.Display();

Output

Deprecateddemo display()

Annotation 4: @Documented

 @Documented is a marker interface that specifies to a tool that a


particular annotation has to be documented.

 Annotations are not included in the ‘Javadoc’ comments. By using


@Documented annotation in the code, tools like Javadoc can process and
include the annotated type in the generated document.

Annotation 5: @Target

 @Target is used as an annotation to another annotation.

 The @Target annotation takes only one argument and this argument
should be a constant value from the ElementType enumeration.

 The constants along with the corresponding declaration ars shown in the
table below:

Target Constant Applied to Annotation


ANNOTATION_TYPE Another Annotation

CONSTRUCTOR Constructor

FIELD Field

LOCAL_VARIABLE Local Variable

METHOD Method

PACKAGE Package

PARAMETER Parameter

Class, Interface, or
TYPE
enumeration

One or more values can be specified in a @Target annotation by using a braces-


delimited list. For example, to specify an annotation that applies to fields and
local variables, the following annotation can be used

@Target({ElementType.FIELD, ElementType.LOCAL_VARIABLE})
Annotation 6: @Inherited

 @Inherited annotation is a marker annotation that is only used on


annotation declaration.

 Only the annotations that are used on class declarations are affected by it.
@Inherited causes the subclass to inherit the annotation from a
superclass. Hence, when there is a request for a specific annotation to a
subclass and it is not present in the subclass, the superclass is checked.

 If the annotation is present in the superclass and it is annotated with


@Inherited, the annotation is returned.

Annotation 7: User-defined (Custom)

 To annotate program elements, i.e. variables, constructors, methods, etc.


user-defined annotations can be used.

 The user-defined annotations can be applied to the elements Ii.e.


variables, constructors, classes, methods) just before their declaration.

 Annotations are created by using @interface and are followed by the


annotation name. An annotation can also have elements.

 They appear as methods but the implementation should not be provided


for these elements. All the annotations extend
java.lang.annotation.Annotation interface. The annotations cannot include
the extended clause.

Example

// Java Program to Demonstrate User-defined Annotations

package source;

import java.lang.annotation.Documented;
import java.lang.annotation.Retention;

import java.lang.annotation.RetentionPolicy;

// User-defined annotation

@Documented

@Retention(RetentionPolicy.RUNTIME)

@ interface AnnotationDemo

String Developer() default "Ravi";

String Expirydate();

} // will be retained at runtime

// Driver class that uses @AnnotationDemo

public class Demo

@AnnotationDemo(Developer="Ravi", Expirydate="26-03-2020")

void fun1()

System.out.println("Demo method 1");

}
@AnnotationDemo(Developer="Kiran", Expirydate="26-03-2021")

void fun2()

System.out.println("Demo method 2");

public static void main(String args[])

System.out.println("Welcome");

Output

Welcome

Use of Annotations

Annotations are used for the following purposes:

1. Instructions to the compiler: There are three types of built-in


annotations @Deprecated, @Override, @SuppressWarnings that can
be used to give instructions to the compiler, detect errors, and suppress
warnings. For example, @Override annotation can be used to instruct
the compiler to denote that the annotated method is overriding the
method.
2. Compile-time Instructions: Software build tools can generate code,
XML files, and more by using the compile-time instructions that the
annotations provide.

3. Runtime Instructions: Annotations can also be defined to provide


instructions to the program at runtime. These annotations can be
accessed using Java Reflection.
Lambda Expressions in Java

 Lambda expression is a new and important feature of Java which was


included in Java SE 8.
 It provides a clear and concise way to represent one method interface
using an expression. It is very useful in collection library. It helps to
iterate, filter and extract data from collection.
 The Lambda expression is used to provide the implementation of an
interface which has functional interface.
 It saves a lot of code. In case of lambda expression, we don't need to
define the method again for providing the implementation. Here, we just
write the implementation code.
 Java lambda expression is treated as a function, so compiler does not
create .class file.
 A lambda expression can implement a functional interface by defining
an anonymous function that can be passed as an argument to some
method also.

Functional Interface

Def:An interface which has only one abstract method is called “functional
interface”.

 Java provides an anotation @FunctionalInterface, which is used to


declare an interface as functional interface.

Note:Lambda expression provides implementation of functional interface.

Example:

1. Runnable interface from package java.lang; is a functional interface


because it constitutes only one method i.e. run().
2. ActionListener interface consists only one method i.e actionPerformed().

Defining a Functional Interface:

Example:

import java.lang.FunctionalInterface;

@FunctionalInterface

public interface MyInterface{


// the single abstract method

double getValue();

Description:

 In the above example, the interface MyInterface has only one abstract
method getValue(). Hence, it is a functional interface.
 Here, we have used the annotation @FunctionalInterface. The annotation
forces the Java compiler to indicate that the interface is a functional
interface. Hence, does not allow to have more than one abstract method.
However, it is not compulsory though.
 In Java 7, functional interfaces were considered as Single Abstract
Methods or SAM type. SAMs were commonly implemented with
Anonymous Classes in Java 7.

Example : Implement SAM with anonymous classes in java

public class FunctionInterfaceTest {

public static void main(String[] args) {

// anonymous class

new Thread(new Runnable() {

@Override

public void run() {

System.out.println("I just implemented the Runnable Functional Interface.");

}).start();

Output:

I just implemented the Runnable Functional Interface.


 Here, we can pass an anonymous class to a method. This helps to write
programs with fewer codes in Java 7. However, the syntax was still
difficult and a lot of extra lines of code were required.
 Java 8 extended the power of a SAMs by going a step further. Since we
know that a functional interface has just one method, there should be no
need to define the name of that method when passing it as an argument.
Lambda expression allows us to do exactly that.

Introduction to Lambda expressions

Def: Lambda expression is, essentially, an anonymous or unnamed method


which does not consists the name, returntype and access modifiers.

 The lambda expression does not execute on its own. Instead, it is used to
implement a method defined by a functional interface.

How to define lambda expression in Java?

Here is how we can define lambda expression in Java.

(argument-list) -> {body} ;

Java lambda expression is consisted of three components.

1) Argument-list: It can be empty or non-empty as well.

2) Arrow-token: It is used to link arguments-list and body of expression.

3) Body: It contains expressions and statements for lambda expression.

Types of Lambda Body

In Java, the lambda body is of two types.

1. A body with a single expression

() -> System.out.println("Lambdas are great");

This type of lambda body is known as the expression body.

2. A body that consists of a block of code.

() -> {

double pi = 3.1415;
return pi;

};

 This type of the lambda body is known as a block body.


 The block body allows the lambda body to include multiple statements.
These statements are enclosed inside the braces and you have to add a
semi-colon after the braces.

Note: For the block body, you can have a return statement if the body returns a
value. However, the expression body does not require a return statement.

Example : Write a Java program that returns the value of Pi using the
lambda expression.

 As mentioned earlier, a lambda expression is not executed on its own.


Rather, it forms the implementation of the abstract method defined by the
functional interface.

So, we need to define a functional interface first.

import java.lang.FunctionalInterface;

// this is functional interface

@FunctionalInterface

interface MyInterface{

// abstract method

double getPiValue();

public class Main {

public static void main( String[] args ) {

// declare a reference to MyInterface

MyInterface ref;

// lambda expression
ref = () -> { return 3.1415;}; (or)

ref = () ->3.1415;

System.out.println("Value of Pi = " + ref.getPiValue());

Output:

Value of Pi = 3.1415

In the above example,

We have created a functional interface named MyInterface. It contains a single


abstract method named getPiValue()

Inside the Main class, we have declared a reference to MyInterface. Note that
we can declare a reference of an interface but we cannot instantiate an interface.
That is,

MyInterface ref = new MyInterface();// it will throw an error

MyInterface ref; // it is valid

We then assigned a lambda expression to the reference.

ref = () -> 3.1415;

Finally, we call the method getPiValue() using the reference interface. When

System.out.println("Value of Pi = " + ref.getPiValue());

Lambda Expressions with parameters

Till now we have created lambda expressions without any parameters.


However, similar to methods, lambda expressions can also have parameters. For
example,

(n) -> (n%2)==0


Here, the variable n inside the parenthesis is a parameter passed to the lambda
expression. The lambda body takes the parameter and checks if it is even or
odd.

Example : Using lambda expression with parameters

@FunctionalInterface

interface MyInterface {

// abstract method

String reverse(String n);

public class Main {

public static void main( String[] args ) {

// declare a reference to MyInterface assign a lambda expression to the reference

MyInterface ref = (str) -> {

String result = "";

for (int i = str.length()-1; i >= 0 ; i--)

result += str.charAt(i);

return result;

};

// call the method of the interface

System. out.println("Lambda reversed = " + ref.reverse("Lambda"));

Output:
Lambda reversed = adbmaL

Why use Lambda Expression

1. To provide the implementation of Functional interface.


2. Less coding.

Java Lambda Expression Syntax

(argument-list) -> {body}

Java lambda expression is consisted of three components.

1) Argument-list: It can be empty or non-empty as well.

2) Arrow-token: It is used to link arguments-list and body of expression.

3) Body: It contains expressions and statements for lambda expression.

Lambda Expression with No Parameter

Syntax

() -> {
//Body of no parameter lambda
}

Lambda Expression with One Parameter Syntax

(p1) -> {
//Body of single parameter lambda
}

Lambda Expression with Two Parameter

Syntax

(p1,p2) -> {
//Body of multiple parameter lambda
}

Implementing an interface without using lambda expression.


interface Drawable{
public void draw();
}
public class LambdaExpressionExample {
public static void main(String[] args) {
int width=10;

//without lambda, Drawable implementation using anonymous class


Drawable d=new Drawable(){
public void draw(){System.out.println("Drawing "+width);
}
};
d.draw();
}
}

Output:

Drawing 10

Java Lambda Expression Example

Now, we are going to implement the above example with the help of Java
lambda expression.

@FunctionalInterface //It is optional


interface Drawable{
public void draw();
}

public class LambdaExpressionExample2 {


public static void main(String[] args) {
int width=10;

//with lambda
Drawable d2=()->{
System.out.println("Drawing "+width);
};
d2.draw();
}
}

Output:

Drawing 10

A lambda expression can have zero or any number of arguments. Let's see the
examples:

Java Lambda Expression Example: No Parameter

interface Sayable{
public String say();
}
public class LambdaExpressionExample3{
public static void main(String[] args) {
Sayable s=()->{
return "I have nothing to say.";
};
System.out.println(s.say());
}
}

Output:

I have nothing to say.

Java Lambda Expression Example: Single Parameter

interface Sayable{
public String say(String name);
}

public class LambdaExpressionExample4{


public static void main(String[] args) {
// Lambda expression with single parameter.
Sayable s1=(name)->{
return "Hello, "+name;
};
System.out.println(s1.say("Sonoo"));

// You can omit function parentheses


Sayable s2= name ->{
return "Hello, "+name;
};
System.out.println(s2.say("Sonoo"));
}
}

Output:

Hello, Sonoo
Hello, Sonoo

Java Lambda Expression Example: Multiple Parameters

interface Addable{
int add(int a,int b);
}

public class LambdaExpressionExample5{


public static void main(String[] args) {

// Multiple parameters in lambda expression


Addable ad1=(a,b)->(a+b);
System.out.println(ad1.add(10,20));

// Multiple parameters with data type in lambda expression


Addable ad2=(int a,int b)->(a+b);
System.out.println(ad2.add(100,200));
}
}

Output:

30
300

Java Lambda Expression Example: with or without return keyword

In Java lambda expression, if there is only one statement, you may or may not
use return keyword. You must use return keyword when lambda expression
contains multiple statements.

interface Addable{
int add(int a,int b);
}

public class LambdaExpressionExample6 {


public static void main(String[] args) {

// Lambda expression without return keyword.


Addable ad1=(a,b)->(a+b);
System.out.println(ad1.add(10,20));

// Lambda expression with return keyword.


Addable ad2=(int a,int b)->{
return (a+b);
};
System.out.println(ad2.add(100,200));
}
}

Output:

30
300

Java Lambda Expression Example: Foreach Loop


import java.util.*;
public class LambdaExpressionExample7{
public static void main(String[] args) {

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


list.add("ankit");
list.add("mayank");
list.add("irfan");
list.add("jai");

list.forEach( (n)->System.out.println(n) );
}
}

Output:

ankit
mayank
irfan
jai

Java Lambda Expression Example: Multiple Statements

@FunctionalInterface
interface Sayable{
String say(String message);
}

public class LambdaExpressionExample8{


public static void main(String[] args) {

// You can pass multiple statements in lambda expression


Sayable person = (message)-> {
String str1 = "I would like to say, ";
String str2 = str1 + message;
return str2;
};
System.out.println(person.say("time is precious."));
}
}

Output:

I would like to say, time is precious.

Java Lambda Expression Example: Creating Thread

You can use lambda expression to run thread. In the following example, we are
implementing run method by using lambda expression.

public class LambdaExpressionExample9{


public static void main(String[] args) {

//Thread Example without lambda


Runnable r1=new Runnable(){
public void run(){
System.out.println("Thread1 is running...");
}
};
Thread t1=new Thread(r1);
t1.start();
//Thread Example with lambda
Runnable r2=()->{
System.out.println("Thread2 is running...");
};
Thread t2=new Thread(r2);
t2.start();
}
}

Output:

Thread1 is running...
Thread2 is running...
Java lambda expression can be used in the collection framework. It provides
efficient and concise way to iterate, filter and fetch data. Following are some
lambda and collection examples provided.

Java Lambda Expression Example: Comparator

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
class Product{
int id;
String name;
float price;
public Product(int id, String name, float price) {
super();
this.id = id;
this.name = name;
this.price = price;
}
}
public class LambdaExpressionExample10{
public static void main(String[] args) {
List<Product> list=new ArrayList<Product>();

//Adding Products
list.add(new Product(1,"HP Laptop",25000f));
list.add(new Product(3,"Keyboard",300f));
list.add(new Product(2,"Dell Mouse",150f));

System.out.println("Sorting on the basis of name...");

// implementing lambda expression


Collections.sort(list,(p1,p2)->{
return p1.name.compareTo(p2.name);
});
for(Product p:list){
System.out.println(p.id+" "+p.name+" "+p.price);
}

}
}

Output:

Sorting on the basis of name...


2 Dell Mouse 150.0
1 HP Laptop 25000.0
3 Keyboard 300.0

Java Lambda Expression Example: Filter Collection Data

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;
classz Product{
int id;
String name;
float price;
public Product(int id, String name, float price) {
super();
this.id = id;
this.name = name;
this.price = price;
}
}
public class LambdaExpressionExample11{
public static void main(String[] args) {
List<Product> list=new ArrayList<Product>();
list.add(new Product(1,"Samsung A5",17000f));
list.add(new Product(3,"Iphone 6S",65000f));
list.add(new Product(2,"Sony Xperia",25000f));
list.add(new Product(4,"Nokia Lumia",15000f));
list.add(new Product(5,"Redmi4 ",26000f));
list.add(new Product(6,"Lenevo Vibe",19000f));
// using lambda to filter data
Stream<Product> filtered_data = list.stream().filter(p -> p.price > 20000);

// using lambda to iterate through collection


filtered_data.forEach( product -> System.out.println(product.name+": "+
product.price)
);
}
}

Output:

Iphone 6S: 65000.0


Sony Xperia: 25000.0
Redmi4 : 26000.0

Sample Programs

1)

@functional_interface

interface Statement {

public String greet();

public class LambdaNP {

public static void main(String[] args) {

Statement s = () -> {

return "Hello World. Welcome to Simplilearn.";

};

System.out.println(s.greet());
}

Passing Lambda Expressions as Arguments


 We can pass lambda expressions as arguments to a function.
 If you have to pass a lambda expression as a parameter, the parameter
type should be able to hold it.
 If you pass an integer as an argument to a function, you must have an int
or Integer parameter. If you are passing an instance of a class as a
parameter, you must specify the class name or the object class as a
parameter to hold the object.

Example 1: Define lambda expressions as method parameters

import java.util.ArrayList;

class Main {

public static void main(String[] args) {

// create an ArrayList

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

// add elements to the ArrayList

languages.add("java");

languages.add("swift");

languages.add("python");

System.out.println("ArrayList: " + languages);

// pass lambda expression as parameter to replaceAll() method

languages.replaceAll(e -> e.toUpperCase());

System.out.println("Updated ArrayList: " + languages);


}

Output

ArrayList: [java, swift, python]

Updated ArrayList: [JAVA, SWIFT, PYTHON]

Note:

1. In the above example, we have created an arraylist named languages.


Notice the line, languages.replaceAll(e -> e.toUpperCase());
2. Here, e -> e.toUpperCase() is a lambda expression. It takes all elements
of the arraylist and converts them into uppercase.

Example 2: Pass multiline lambda body as function arguments

import java.util.ArrayList;

import java.util.Arrays;

class Main {

public static void main(String[] args) {

// create an ArrayList

ArrayList<String> languages = new ArrayList<>(Arrays.asList("java",


"python"));

System.out.println("ArrayList: " + languages);

// call the foEach() method

// pass lambda as argument fo forEach()

// reverse each element of ArrayList

System.out.print("Reversed ArrayList: ");

languages.forEach((e) -> {
// body of lambda expression

String result = "";

for (int i = e.length()-1; i >= 0 ; i--)

result += e.charAt(i);

System.out.print(result + ", ");

});

Output:

ArrayList: [java, python]

Reversed ArrayList: avaj, nohtyp,

Note:

1. In the above example, we have created an arraylist languages. Notice the


line,

languages.forEach((e) -> {

// body of lambda expression

String result = "";

for (int i = e.length()-1; i >= 0 ; i--)

result += e.charAt(i);

System.out.print(result + ", ");

});

2. Here, we are passing lambda expression as an argument to the ArrayList


forEach() method. The lambda expression will reverse each element of
the arraylist.
Lambda Expressions and Exceptions

 There are times when an exception can occur in lambda expressions. In


this case, the lambda expression should throw an exception.
 Throwing an exception by a lambda expression is implemented in the
standard way using the throw statement.
 The fragment to be checked in the lambda expression is placed in a try-
catch block.

As with a method, you can throw two kinds of exceptions in a lambda


expression:

 predefined Java
types(ArithmeticException,ArrayIndexOutOfBoundsException,etc.);
 self-developed exceptions classes.

1.Examples generating standard exception in lambda expression


Division of two numbers. Throwing a division by zero exception

The example provides a lambda expression that returns the result of dividing
two numbers. The lambda expression code checks the divisor value. If this
value is 0, then a standard ArithmeticException is thrown with an appropriate
message.

// Generating exceptions in lambda expressions

// Declare a functional interface

interface IDivNumbers {

// Declare a method that divides two numbers

double Division(double a, double b);

// A class that contains methods that implement


// lambda expressions and test the program.

public class Lambda {

public static void main(String[] args) {

// 1. Declare a reference to IDivNumbers

IDivNumbers ref;

// 2. Implement a lambda expression that divides two numbers

// and throws an exception if necessary

ref = (a, b) -> {

try {

// Process division by 0 check

if (b==0)

throw new ArithmeticException("Exception: divide by zero.");

return a/b; // if b! = 0, then return the result of division

catch (ArithmeticException e) {

System.out.println(e.getMessage()); // Print the error message

return 0.0;

};

// 3. Test lambda expression for exception

double res = ref.Division(5, 0); // Exception: divide by zero.

// 4. Print the result


System.out.println("res = " + res); // res = 0.0

The result of the program

Exception: divide by zero.

res = 0.0

Example2: An example of throwing an exception that is handled by a


specially designed class

Develop a NegativeRootException class that handles the exception thrown


when an attempt is made to take the square root of a negative number.
Implement a lambda expression that calculates the area of a triangle from the
lengths of its sides. The calculation is based on Heron’s formula. If a root of a
negative number occurs in Heron’s formula (the triangle does not exist), then
throw a NegativeRootException. The exception must be thrown in a lambda
expression.

// Generating exceptions in lambda expressions

// Declare a functional interface

interface INegativeRoot {

// Declare a method that determines the area of a triangle by its sides

double AreaTriangle(double a, double b, double c);

// Create an exception class that handles the square root of a negative number

// Such an exception is checked.

class NegativeRootException extends Exception {


// Class constructor

NegativeRootException(String message) {

super(message); // Call the constructor of superclass Exception

// A class that contains methods that implement the lambda

// expression and test the operation of the program.

public class Lambda {

@SuppressWarnings("finally")

public static void main(String[] args) {

// 1. Declare a reference to INegativeRoot

INegativeRoot ref = null;

// 2. Implement a lambda expression that calculates

// the area of a triangle using Heron's formula

ref = (a, b, c) -> {

double p = (a+b+c)/2; // semiperimeter

double t = p*(p-a)*(p-b)*(p-c); // root expression

try {
// check for negative root with throwing an exception

if (t<0) {

t=0;

throw new NegativeRootException("Exception: negative root.");

return Math.sqrt(t);

catch (NegativeRootException e) {

t = 0;

System.out.println(e.getMessage());

finally {

// Block that is always executed

return t;

};

// 3. Test lambda expression for exception

double res = ref.AreaTriangle(1, 1, 5); // Exception: negative root.

// 4. Print the result

System.out.println("res = " + res); // res = 0.0

}
}

After execution, the program will give the following result

Exception: negative root.

res = 0.0

Lambda Expression Variable Capturing with


Examples
 Variable defined by the enclosing scope of a lambda expression are
accessible within the lambda expression.
 For example, a lambda expression can use an instance or static variable
defined by its enclosing class.
 A lambda expression also has access to (both explicitly and implicitly),
which refers to the invoking instance of the lambda expression’s
enclosing class.
 However, when a lambda expression uses a local variable from its
enclosing scope, a special situation is created that is referred to as a
variable capture.
 In this case, a lambda expression may only use local variables that are
effectively final.
 An effectively final variable is one whose value does not change
after it is first assigned.
 There is no need to explicitly declare such a variable as final, although
doing so would not be an error.
 It is important to understand that a local variable of the enclosing scope
cannot be modified by the lambda expression. Doing so would remove
its effectively final status, thus rendering it illegal for capture.
There are certain keypoints to be remembered, which are as follows:
1. Any local variable, formal parameter, or exception parameter used
but not declared in a lambda expression must either be declared final
or be effectively final , or a compile-time error occurs where the use
is attempted.
2. Any local variable used but not declared in a lambda body must be
definitely assigned before the lambda body, or a compile-time error
occurs.

Example: // Java Program Illustrating Difference between Effectively final

import java.util.function.Function;

public class Main {

public static void main(String[] argv) {

String x= "Hello";

Function<String,String> func1 = y -> {

x="a";

return y + " "+ x ;};

System.out.println(func1.apply("CVR College"));

Example 2:

// Java Program Illustrating Difference between

// Effectively final and Mutable Local Variables


// Importing input output classes

import java.io.*;

// Interface

interface MyInterface {

// Method inside the interface

void myFunction();

// Main class

class GFG {

// Custom initialization

int data = 170;

// Main driver method

public static void main(String[] args)

// Creating object of this class

// inside the main() method

GFG gfg = new GFG();

// Creating object of interface

// inside the main() method

MyInterface intFace = () ->

System.out.println("Data : " + gfg.data);


gfg.data += 500;

System.out.println("Data : " + gfg.data);

};

intFace.myFunction();

gfg.data += 200;

System.out.println("Data : " + gfg.data);

Output:

Data : 170
Data : 670
Data : 870

Method References in Java


 In Java 8 we can use the method as if they were objects or primitive
values, and we can treat them as a variable.
 Method references are used to refer methods of other classes instead of
implementing a Functional interface using anonymous classes or lambda
expressions.

Ex: Assume we have a functional interface as

Interface Calculator

Calculate();
}

 To implement this we can use either anonymous class or lambda


expression.
 But if there is a method with same functionality that is expected by a
calculate() method.
 Then instead of using lambda expression we can refer to that particular
method.

Def: So,referring to a static (or) non-static methods(or) constructors instead


of implementing lambda expression or anonymous classes is calles
“Method References”.

Note:

 Method references are compact and more readable.


 We use “::” operator for method reference.

Generic syntax: Method reference

A. To refer to a method in an object


Object :: methodName
B. To print all elements in a list
 Following is an illustration of a lambda expression that just calls a
single method in its entire execution:
list.forEach(s -> System.out.println(s));
C. Shorthand to print all elements in a list
 To make the code clear and compact, In the above example, one can
turn lambda expression into a method reference:
list.forEach(System.out::println);

Types of Method References


There are four type method references that are as follows:
1. Static Method Reference.
2. Instance Method Reference of a particular object.
3. Instance Method Reference of an arbitrary object of a particular type.
4. Constructor Reference.
Type 1: Reference to a static method

 If a Lambda expression is like:


// If a lambda expression just call a static method of a class
(args) -> Class.staticMethod(args)
 Then method reference is like:
// Shorthand if a lambda expression just call a static method of a class
Classname::staticMethod

Example:
MethodRefDemo.java
//Java Program to Illustrate How One can use Static method reference

interface Calculator
{
void calculate(int x,int y);
}
class Scientific
{
public static void findSum(int x,int y)
{
System.out.println("Sum="+(x+y));
}
}
class MethodRefDemo
{
public static void main(String args[])
{
//Reference to a static method
Calculator calc=Scientific::findSum;
calc.calculate(10,20);
}
}

Output:
Type 2: Reference to an instance method of a particular object
 If a Lambda expression is like:
// If a lambda expression just call a default method of an object
(args) -> obj.instanceMethod(args)
 Then method reference is like:
// Shorthand if a lambda expression just call a default method of an
object
objname::instanceMethod

Example:

interface Calculator
{
void calculate(int x,int y);
}
class Scientific
{
public static void findSum(int x,int y)
{
System.out.println("Sum="+(x+y));
}
public void findProduct(int x,int y)
{
System.out.println("Product="+(x*y));
}
}
class MethodRefDemo
{
public static void main(String args[])
{
//Reference to a static method
Calculator calc1=Scientific::findSum;
calc1.calculate(10,20);
//Referencing Instance method
Scientific sc=new Scientific();
Calculator calc2=sc::findProduct;
calc2.calculate(10,20);
}
}

Output:

Type 4: Constructor method reference


 If a Lambda expression is like:
 // If a lambda expression just create an object
(args) -> new ClassName(args)
 Then method reference is like:
// Shorthand if a lambda expression just create an object
ClassName::new

Example:

interface ShapeFactory

Shape getShape(int x);

class Shape

int x;

public Shape(int x)

this.x=x;

void area()

System.out.println("Area="+(x*x));

class MethodConRefDemo

public static void main(String args[])


{

ShapeFactory factory=Shape::new;

Shape shape=factory.getShape(10);

shape.area();

Output:

Type 3: Reference to an instance method of an arbitrary object of a


particular type
 If a Lambda expression is like:
// If a lambda expression just call an instance method of a ObjectType
(obj, args) -> obj.instanceMethod(args)
 Then method reference is like:
// Shorthand if a lambda expression just call an instance method of a
ObjectType
ObjectType::instanceMethod

Example:

//Reference to an instance method of an Arbitrary

//object of a particular type

import java.util.*;

class User

String name;

public User(String name)

this.name=name;

void Print()

System.out.println(name);

class MethArbitrary

public static void main(String args[])

Arrays.asList(new User("Rama"),new User("Kumaran")).stream().


forEach((User)->User.Print());

//using method reference

Arrays.asList(new User("Rama"),new User("Kumaran")).stream().

forEach((User::Print));

Output:
Modules
Basics of modules
Advantages of Java SE 9 Module System

Java SE 9 Module System is going to provide the following benefits

 As Java SE 9 is going to divide JDK, JRE, JARs etc, into smaller modules, we
can use whatever modules we want. So it is very easy to scale down the Java
Application to Small devices.
 Ease of Testing and Maintainability.
 Supports better Performance.
 As public is not just public, it supports very Strong Encapsulation. (Don’t worry
its a big concept. we will explore it with some useful examples soon).
 We cannot access Internal Non-Critical APIs anymore.
 Modules can hide unwanted and internal details very safely, we can get better
Security.
 Application is too small because we can use only what ever modules we want.
 Its easy to support Less Coupling between components.
 Its easy to support Single Responsibility Principle (SRP).

We will explore all these concepts one by one soon.

Compare JDK 8 and JDK 9

We know what a JDK software contains. After installing JDK 8 software, we


can see a couple of directories like bin, jre, lib etc in Java Home folder.
However, Oracle Corp has changed this folder structure a bit differently as
shown below. JDK
8 Folder Structure:

JDK 9 Folder

Structure: Here
JDK 9 does NOT contain JRE. In JDK 9, JRE is separated into a separate
distribution folder. JDK 9 software contains a new folder “jmods”. It contains a
set of Java 9 Modules as shown below. In JDK 9, No rt.jar and No tools.jar

NOTE:- As of
today, “jmods” contains 95 modules. It may increase in the final release.
“jmods” folder is available at ${JAVA_HOME}/jmods. These are known as
JDK Modules.

What is Java 9 Module?

A Module is a self-describing collection of Code, Data, and some Resources. It


is a set of related Packages, Types (classes, abstract classes, interfaces etc) with
Code & Data and Resources. Each Module contains only a set of related code
and data to support Single Responsibility (Functionality) Principle (SRP).

The main goal of Java 9 Module System is to support Modular


Programming in Java.

Mother of Java 9 Module System

As of now, Java 9 Module System has 95 modules in Early Access JDK. Oracle
Corp has separated JDK jars and Java SE Specifications into two set of
Modules.

 All JDK Modules starts with “jdk.*”


 All Java SE Specifications Modules starts with “java.*”

Java 9 Module System has a “java.base” Module. It’s known as Base Module.
It’s an Independent module and does NOT dependent on any other modules. By
default, all other Modules dependent on this module. That’s why “java.base”
Module is also known as The Mother of Java 9 Modules. It’s default module for
all JDK Modules and User-Defined Modules.

Compare Java 8 and Java 9 Applications

We have already developed many Java applications using Java 5, 6,7, or 8. We


know how a Java 8 or earlier applications looks like and what it contains. In
brief, I have depicted a Java 8 applications in a diagram as shown below:

In a Java 8 or earlier applications, Top level component a Package. It groups a


set related to types into a group. It also contains a set of resources. Java 9
Applications does not have much difference with this. It just introduced a new
component called “Module”, which is used to group a set of related Packages
into a group. And one more new component that Module Descriptor (“module-
info.java”). That’s it. Rest of the application is same as earlier versions of
applications as shown below.

requires
The requires directive indicates that this module depends on another module.
We used this directive in the previous posts, where the
module com.mydeveloperplanet.jpmshello depends on
module com.mydeveloperplanet.jpmshi.
1
requires com.mydeveloperplanet.jpmshi;

The requires directive also knows two variants:

 requires transitive <module name>: This means that any module that
reads your module implicitly also reads the transitive module — for
example, if your module contains a method which is publicly available
and returns a type of another module. When transitive is not used, any
module reading your module would explicitly have to add the dependent
module.
 requires static <module name>: This is an optional dependency. The
module is needed at compile time, but not at runtime.

exports
The exports directive indicates which public types of the module's package are
accessible to other modules. We used this directive in the previous posts where
the package com.mydeveloperplanet.jpmshi was made accessible so that
module com.mydeveloperplanet.jpmshello was able to use the HiModules class.
exports com.mydeveloperplanet.jpmshi;

Also, an exports...to directive exists. After the to keyword, it is possible to set


the list of packages that are allowed to use the exported packages.

Compiling a Java Module


In order to compile a Java module you need to use the javac command that
comes with the Java SDK. Remember, you need Java 9 or later to compile a
Java module.

javac -d out --module-source-path src/main/java --module com.jenkov.mymodule

Remember you must have the javac command from the JDK installation on
your path (environment variable) for this command to work. Alternatively you
can replace the javac part in the command above with the full path to where
the javac command is located, like this:

"C:\Program Files\Java\jdk-9.0.4\bin\javac" -d out --module-source-path src/main/java --


module com.jenkov.mymodule

Or
Javac –module-source-path src –d out –m moduleA

Running a Java Module


In order to run the main class of a Java module you use the java command, like
this:

java --module-path out --module com.jenkov.mymodule/com.jenkov.mymodule.Main

Or

Java –module-path out –m modulaA/pack1.Test

The --module-path argument points to the root directory where all the compiled
modules are located. Remember, this is one level above the module root
directory.
The --module argument tells what module + main class to run. In the example
the module name is the com.jenkov.mymodule part and the main class name is
the com.jenkov.mymodule.Main . Notice how the module name and main class
name are separated by a slash (/) character.

Unnamed Module
 From Java 9 and forward, all Java classes must be located in a module for
the Java VM to use them. But what do you do with older Java libraries
where you just have the compiled classes, or a JAR file?
 In Java 9 you can still use the -classpath argument to the Java VM when
running an application. On the classpath you can include all your older
Java classes, just like you have done before Java 9. All classes found on
the classpath will be included in what Java calls the unnamed module.
 The unnamed module exports all its packages. However, the classes in
the unnamed module are only readable by other classes in the unnamed
module - or from automatic modules (see next section). No named
module can read the classes of the unnamed module.
 If a package is exported by a named module, but also found in the
unnamed module, the package from the named module will be used.
 All classes in the unnamed module requires all modules found on the
module path. That way, all classes in the unnamed module can read all
classes exported by all the Java modules found on the module path.

Example:
 Go to src directory

Now instead of creating aa a module if we try to execute as normal application as


Note: We will get the output.
Java Platform Module System

Introduction
Java 9 introduced the concept of modules to the Java platform. Modules change
the way we design and build Java applications. Even though their use is
optional, the JDK itself is now modularized, so we must at least know the basics
of the Java Platform Module System (JPMS).

The module system introduced in Java 9 makes it easier to organize your code.
Here’s a brief guide to working with modules in Java

The Java Platform Module System (JPMS) is a code-level structure, so it


doesn’t change the fact that we package Java into JAR files. Ultimately,
everything is still bundled together in JAR files. The module system adds a
new, higher-level descriptor that JARs can use, by incorporating the module-
info.java file.

Class path vs. module path

In Java until now the class path has been the bottom line for what is available to
a running application. Although the class path serves this purpose and is well
understood, it ends up being a big, undifferentiated bucket into which all
dependencies are placed.
The module path adds a level above the class path. It serves as a container for
packages and determines what packages are available to the application.

Services and Service Providers

In Java 9, we can develop Services and Service Providers as modules. A service


module declares that it uses one or more interfaces whose implementations will be
provided at run time by some provider modules. A provider module declares what
implementations of service interfaces it provides.

We still have the option to deploying service providers on the class path (check out this
example).

Deploying Services and Service Providers as modules

The Service Module

Let's say we have a service interface com.service.api.AnInterface. To declare this


interface as service provider interface (SPI), we will use the "uses" clause in module-
info.java:

module com.service.api {
exports com.service.api;
uses com.service.api.AnInterface;
}

The Service Provider Module

A service provider will use "provides .. with" clause to declare what service interface it
intends to use (by using provides keyword) and what implementation of the interface it
wants to expose (by using with keyword).

module com.service.provider {
requires msg.service.api;
provides com.service.api.AnInterface with com.service.provider.AnInterfaceImpl;
}

We don't have to specify the service implementation in a file under the resource
directory META-INF/services. (Without modules, we still have to do that).

The Client Application using the Service


module com.example.app {
requires com.service.api;
}

Client application should be unaware of the provider implementation at compile time.


We can deploy any providers during runtime via 'module-path'.

Discovering the Service implementations

We still need to use ServiceLoader#load(AnInterface.class) to discover service


implementation instances. This is typically done in service API module itself.

Let's see a complete example to understand how to do that.

Example
In this example, we will create very simple service API module 'msg.service.api' which
'uses' a service interface to display a message. The example Provider will implement
the interface and will show the message in a Java Swing dialog. We will also create a
third project which will require the service interface and will have the provider
deployed during runtime via module path.

The Service API


msg.service.api/src/msg/service/MsgService.java
package msg.service;

import java.util.ArrayList;
import java.util.List;
import java.util.ServiceLoader;
public interface MsgService {

static List<MsgService> getInstances() {


ServiceLoader<MsgService> services = ServiceLoader.load(MsgService.class);
List<MsgService> list = new ArrayList<>();
services.iterator().forEachRemaining(list::add);
return list;
}

void showMessage(String msg);


}

msg.service.api/src/module-info.java
module msg.service.api {
exports msg.service;
uses msg.service.MsgService;
}

D:\java-modules\java-service-loader-example\msg.service.api>"tree /A /F"
|
\---src
| module-info.java
|
\---msg
\---service
MsgService.java

Compiling the class:

D:\java-modules\java-service-loader-example\msg.service.api>javac -d out src/module-


info.java src/msg/service/MsgService.java

Creating the jar:


D:\java-modules\java-service-loader-example\msg.service.api>jar --create --file msg-
service.jar -C out .

Now we have following files:

D:\java-modules\java-service-loader-example\msg.service.api>"tree /A /F"
| msg-service.jar
|
+---out
| | module-info.class
| |
| \---msg
| \---service
| MsgService.class
|
\---src
| module-info.java
|
\---msg
\---service
MsgService.java

The Service Provider


msg.service.provider.swing/src/msg/provider/swing/MsgServiceImpl.java
package msg.provider.swing;

import msg.service.MsgService;
import javax.swing.*;

public class MsgServiceImpl implements MsgService{


@Override
public void showMessage(String msg) {
JOptionPane.showMessageDialog(null, msg);
}
}

msg.service.provider.swing/src/module-info.java
module msg.service.provider.swing {
requires msg.service.api;
requires java.desktop;
provides msg.service.MsgService with msg.provider.swing.MsgServiceImpl;
}

Above module is also requiring 'java.desktop' which contains javax.swing.

Following is the directory structure of the provider project:

D:\java-modules\java-service-loader-example/msg.service.provider.swing>"tree
/A /F"
|
\---src
| module-info.java
|
\---msg
\---provider
\---swing
MsgServiceImpl.java

Copying msg-lib.jar(the service API) to msg.service.provider.swing/lib, so that


we can compile the provider classes:

D:\java-modules\java-service-loader-
example\msg.service.provider.swing>mkdir lib
D:\java-modules\java-service-loader-example\msg.service.provider.swing>copy
..\msg.service.api\msg-service.jar lib\msg-service.jar
1 file(s) copied.
D:\java-modules\java-service-loader-example\msg.service.provider.swing>"tree
/A /F"
|
+---lib
| msg-service.jar
|
\---src
| module-info.java
|
\---msg
\---provider
\---swing
MsgServiceImpl.java

Compiling the classes:

D:\java-modules\java-service-loader-
example\msg.service.provider.swing>javac -d out --module-path lib
src/module-info.java src/msg/provider/swing/MsgServiceImpl.java

Creating the jar:

D:\java-modules\java-service-loader-example\msg.service.provider.swing>jar --
create --file msg-service-swing.jar -C out .
D:\java-modules\java-service-loader-example\msg.service.provider.swing>"tree
/A /F"
| msg-service-swing.jar
|
+---lib
| msg-service.jar
|
+---out
| | module-info.class
| |
| \---msg
| \---provider
| \---swing
| MsgServiceImpl.class
|
\---src
| module-info.java
|
\---msg
\---provider
\---swing
MsgServiceImpl.java

The Service Client Application


my-app/src/com/logicbig/AppMain.java
package com.logicbig;

import msg.service.MsgService;

import java.util.List;

public class AppMain {


public static void main(String[] args) {
List<MsgService> msgServices = MsgService.getInstances();
for (MsgService msgService : msgServices) {
msgService.showMessage("A test message");
}
}
}

my-app/src/module-info.java
module my.app {
requires msg.service.api;
}

D:\java-modules\java-service-loader-example/my-app>"tree /A /F"
|
\---src
| module-info.java
|
\---com
\---logicbig
AppMain.java

Copying msg-lib.jar (Service API) to my-app/lib/msg-lib.jar

D:\java-modules\java-service-loader-example\my-app>mkdir lib
D:\java-modules\java-service-loader-example\my-app>copy
..\msg.service.api\msg-service.jar lib\msg-service.jar
1 file(s) copied.

Copying msg-service-swing.jar (the provider) to my-app/lib/msg-service-


swing.jar:

D:\java-modules\java-service-loader-example\my-app>copy
..\msg.service.provider.swing\msg-service-swing.jar lib\msg-service-swing.jar
1 file(s) copied.
D:\java-modules\java-service-loader-example\my-app>"tree /A /F"
|
+---lib
| msg-service-swing.jar
| msg-service.jar
|
\---src
| module-info.java
|
\---com
\---logicbig
AppMain.java

Compiling the classes:

D:\java-modules\java-service-loader-example\my-app>javac -d out --module-


path lib src/module-info.java src/com/logicbig/AppMain.java

Running the application


D:\java-modules\java-service-loader-example\my-app>java --module-path
out;lib --module my.app/com.logicbig.AppMain

You might also like