Unit 3 Notes
Unit 3 Notes
Unit 3
Lecture 21
Lecture 21
• Functional Interfaces
• Lambda Expression
Functional Interfaces
• A functional interface is an interface that contains
only one abstract method.
• They can have only one functionality to exhibit.
• From Java 8 onwards, lambda expressions can
be used to represent the instance of a functional
interface.
• A functional interface can have any number of
default methods.
• Runnable, ActionListener, and Comparable are
some of the examples of functional interfaces.
• Functional Interface is additionally
recognized as Single Abstract Method
Interfaces.
• Zero Parameter
• Single Parameter
• Multiple Parameters
1. Lambda Expression with Zero parameter
() -> System.out.println("Zero parameter lambda");
if(isEven.test(10)) System.out.println("10 is
even");
if(!isEven.test(9)) System.out.println("9 is not
even");
}
}
Passing Lambda Expression as arguments
@FunctionalInterface
public interface BinaryOperator<T> extends BiFunction<T, U,
R>
{
……...
}
4. Supplier
• The Supplier functional interface is also a
type of functional interface that does not
take any input or argument and yet returns
a single output.
• This type of functional interface is generally
used in the lazy generation of values.
• Supplier functional interfaces are also used
for defining the logic for the generation of
any sequence.
For example –
The logic behind the Fibonacci Series can be
generated with the help of the Stream. generate
method, which is implemented by the Supplier
functional Interface.
Unit 3
Lecture 22
Lecture 22
• Method References
• Stream API
• Default Methods
Method References
• A method reference is a compact and clear lambda
expression for calling a method
• Method references are a shorthand notation of a
lambda expression to call a method. They provide a
clean and concise way to refer to methods without
executing them.
• Method references were introduced in Java 8 and are
often used with functional interfaces and streams.
ClassName::methodName
Types of Method References
There are following types of method references in
java:
• Reference to a static method.
• Reference to an instance method.
• Reference to a constructor.
1) Reference to a Static Method
You can refer to static method defined in the
class.
Syntax
ContainingClass::staticMethodName
Defined a functional interface and referring a static method
interface Sayable{
void say();
}
public class MethodReference {
public static void saySomething(){
System.out.println("Hello, this is static method.");
}
public static void main(String[] args) {
// Referring static method
Sayable sayable = MethodReference::saySomething;
// Calling interface method
sayable.say();
}
}
2) Reference to an Instance Method
like static methods, we can also refer instance
methods.
Syntax
containingObject::instanceMethodName
interface Sayable{
void say();
}
public class InstanceMethodReference {
public void saySomething(){
System.out.println("Hello, this is non-static
method.");
}
}
class MyInstanceMethodReference
{ public static void main(String[] args) {
InstanceMethodReference methodReference = new
InstanceMethodReference(); // Creating object
// Referring non-static method using reference
Sayable sayable =
methodReference::saySomething;
// Calling interface method
sayable.say();
}
}
3) Reference to a Constructor
We can refer a constructor by using the new
keyword. Here, we are referring constructor with the
help of functional interface.
Syntax
ClassName::new
interface Messageable{
Message getMessage(String msg);
}
class Message{
Message(String msg){
System.out.print(msg);
} }
public class ConstructorReference {
public static void main(String[] args) {
Messageable hello = Message::new;
hello.getMessage("Hello");
}
}
Java 8 Stream
• Java provides a new additional package in Java 8
called java.util.stream.
• This package consists of classes and interfaces
allows functional-style operations on the
elements.
• We can use stream by importing java.util.stream
package.
• Stream does not store elements.
• It simply conveys elements from a source such
as a data structure, an array, or an I/O channel,
through a pipeline of computational
operations.
• Stream is functional in nature.
• Operations performed on a stream does not
modify it's source.
For example, filtering a Stream obtained from a
collection produces a new Stream without the
filtered elements, rather than removing elements
from the source collection.
• The elements of a stream are only visited once
during the life of a stream.
• Like an Iterator, a new stream must be
generated to revisit the same elements of the
source.
Methods Description
System.out.println(productPriceList2);
}
}
Java Stream Iterating Example
import java.util.stream.*;
public class JavaStreamExample {
public static void main(String[] args){
Stream.iterate(1, element->element+1)
.filter(element->element%5==0)
.limit(5)
.forEach(System.out::println);
}
}
Filtering and Iterating Collection
public class JavaStreamExample {
public static void main(String[] args) {
List<Product> productsList = new ArrayList<Product>();
//Adding Products
productsList.add(new Product(1,"HP Laptop",25000f));
productsList.add(new Product(2,"Dell Laptop",30000f));
productsList.add(new Product(3,"Lenevo Laptop",28000f));
productsList.add(new Product(4,"Sony Laptop",28000f));
productsList.add(new Product(5,"Apple Laptop",90000f));
// This is more compact approach for filtering data
productsList.stream()
.filter(product -> product.price == 30000)
.forEach(product -> System.out.println(product.name));
}
}
Java Stream Example: count() Method
public class JavaStreamExample {
public static void main(String[] args) {
List<Product> productsList = new ArrayList<Product>();
//Adding Products
productsList.add(new Product(1,"HP Laptop",25000f));
productsList.add(new Product(2,"Dell Laptop",30000f));
productsList.add(new Product(3,"Lenevo Laptop",28000f));
productsList.add(new Product(4,"Sony Laptop",28000f));
productsList.add(new Product(5,"Apple Laptop",90000f));
// count number of products based on the filter
long count = productsList.stream()
.filter(product->product.price<30000)
.count();
System.out.println(count);
}
}
Java Default Methods
• Java provides a facility to create default
methods inside the interface.
• Methods which are defined inside the interface
and tagged with default are known as default
methods.
• These methods are non-abstract methods.
Java Default Method Example
1. interface Sayable{
2. // Default method
3. default void say(){
4. System.out.println("Hello, this is default method");
5. }
6. // Abstract method
7. void sayMore(String msg);
8. }
9. public class DefaultMethods implements Sayable{
10. public void sayMore(String msg){
11. // implementing abstract method
12. System.out.println(msg);
13. }
public class DefaultMethods implements Sayable{
public void sayMore(String msg){
// implementing abstract method
System.out.println(msg);
}
public static void main(String[] args) {
DefaultMethods dm = new DefaultMethods();
dm.say(); // calling default method
dm.sayMore("Work is worship");
// calling abstract method
}
}
Object Oriented Programming with Java
(Subject Code: BCS-403)
Unit 3
Lecture 23
Lecture 23
• Static Method
• Base64 Encode and Decode
Static method in Interface in Java
• Static Methods in Interface are those methods,
which are defined in the interface with the
keyword static.
• Unlike other methods in Interface, these static
methods contain the complete definition of the
function and since the definition is complete and
the method is static, therefore these methods
cannot be overridden or changed in the
implementation class.
• Similar to Default Method in Interface, the static
method in an interface can be defined in the
interface, but cannot be overridden in
Implementation Classes.
• To use a static method, Interface name should be
instantiated with it, as it is a part of the Interface
only.
interface NewInterface {
// static method
static void hello()
{
System.out.println("Hello, New Static Method Here");
}
// Public and abstract method of Interface
void overrideMethod(String str);
}
// Implementation Class
public class InterfaceDemo implements NewInterface {
public static void main(String[] args)
{
InterfaceDemo interfaceDemo = new InterfaceDemo();
// Calling the static method of interface
NewInterface.hello();
// Calling the abstract method of interface
interfaceDemo.overrideMethod("Hello, Override Method here");
}
// Implementing interface method
@Override
public void overrideMethod(String str)
{
System.out.println(str);
}
}
To demonstrate Scope of Static method
• The scope of the static method definition is
within the interface only.
• If same name method is implemented in the
implementation class then that method
becomes a static member of that respective
class.
Program to Demonstrate scope of static method in Interface.
interface PrintDemo {
// Static Method
static void hello()
{
System.out.println("Called from Interface PrintDemo");
}
}
public class InterfaceDemo implements PrintDemo {
public static void main(String[] args)
{
// Call Interface method as Interface
// name is preceding with method
PrintDemo.hello();
// Call Class static method
hello();
}
// Class Static method is defined
static void hello()
{
System.out.println("Called from Class");
}
}
Java Base64 Encode and Decode
• Java provides a class Base64 to deal with encryption.
• We can encrypt and decrypt data by using provided
methods.
• We need to import java.util.Base64 in source file to
use its methods.
• It uses the Base64 alphabet specified by Java in RFC
4648 and RFC 2045 for encoding and decoding
operations.
• The encoder does not add any line separator
character. The decoder rejects data that contains
characters outside the base64 alphabet.
Nested Classes of Base64
Class Description
public byte[] decode(byte[] src) It decodes all bytes from the input
byte array using the Base64 encoding
scheme, writing the results into a
newly-allocated output byte array.
The returned byte array is of the
length of the resulting bytes.
public byte[] encode(byte[] src) It encodes all bytes from the specified
byte array into a newly-allocated byte
array using the Base64 encoding
scheme. The returned byte array is of
the length of the resulting bytes.
public int encode(byte[] src, byte[] dst) It encodes all bytes from the specified
byte array using the Base64 encoding
scheme, writing the resulting bytes to
the given output byte array, starting at
offset 0.
Basic Encoding and Decoding
import java.util.Base64;
public class Base64BasicEncryptionExample {
public static void main(String[] args) {
// Getting encoder
Base64.Encoder encoder = Base64.getEncoder();
// Creating byte array
byte byteArr[] = {1,2};
// encoding byte array
byte byteArr2[] = encoder.encode(byteArr);
System.out.println("Encoded byte array: "+byteArr2);
byte byteArr3[] = new byte[5];
int x = encoder.encode(byteArr,byteArr3);
System.out.println("Encoded byte array written to another array:
"+byteArr3);
System.out.println("Number of bytes written: "+x);
}}
Output
Encoded byte array: [B@15db9742
Encoded byte array written to another array:
[B@6d06d69c
Number of bytes written: 4
Basic Encoding and Decoding
import java.util.Base64;
public class Base64BasicEncryptionExample1 {
public static void main(String[] args) {
// Getting encoder
Base64.Encoder encoder = Base64.getEncoder();
// Encoding string
String str = encoder.encodeToString(“KIET Group of
Institutions".getBytes());
System.out.println("Encoded string: "+str);
// Getting decoder
Base64.Decoder decoder = Base64.getDecoder();
// Decoding string
String dStr = new String(decoder.decode(str));
System.out.println("Decoded string: "+dStr);
}}
URL Encoding and Decoding
import java.util.Base64;
public class Base64URLEncryptionExample {
public static void main(String[] args) {
// Getting encoder
Base64.Encoder encoder = Base64.getUrlEncoder();
// Encoding URL
String eStr = encoder.encodeToString("https://kiet.edu/".getBytes());
System.out.println("Encoded URL: "+eStr);
// Getting decoder
Base64.Decoder decoder = Base64.getUrlDecoder();
// Decoding URl
String dStr = new String(decoder.decode(eStr));
System.out.println("Decoded URL: "+dStr);
}
}
Object Oriented Programming with Java
(Subject Code: BCS-403)
Unit 3
Lecture 24
Lecture 24
• ForEach Method
• Try-with resources
• Type Annotations, Repeating Annotations
forEach loop
• Java provides a new method forEach() to iterate
the elements.
• It is defined in Iterable and Stream interface.
• It is a default method defined in the Iterable
interface.
• Collection classes which extends Iterable
interface can use forEach loop to iterate
elements.
forEach() Signature in Iterable Interface
default void forEach(Consumer<super T>action)
Java 8 forEach() example
import java.util.ArrayList;
import java.util.List;
public class ForEachExample {
public static void main(String[] args) {
List<String> gamesList = new ArrayList<String>();
gamesList.add("Football");
gamesList.add("Cricket");
gamesList.add("Chess");
gamesList.add("Hocky");
gamesList.forEach(System.out::println);
} }
Java Stream forEachOrdered() Method Example
import java.util.ArrayList;
import java.util.List;
public class ForEachOrderedExample {
public static void main(String[] args) {
List<String> gamesList = new ArrayList<String>();
gamesList.add("Football");
gamesList.add("Cricket");
gamesList.add("Chess");
gamesList.add("Hocky");
System.out.println("------Iterating by passing lambda expression---------------");
gamesList.stream().forEachOrdered(games -> System.out.println(games));
System.out.println("------Iterating by passing method reference---------------");
gamesList.stream().forEachOrdered(System.out::println);
} }
Try-with-resources
• Try-with-resources statement is a try statement
that declares one or more resources in it.
• A resource is an object that must be closed once
your program is done using it.
• For example, a File resource or a Socket
connection resource.
• The try-with-resources statement ensures that
each resource is closed at the end of the
statement execution.
• If we don’t close the resources, it may constitute a
resource leak and also the program could exhaust
the resources available to it.
• We can pass any object as a resource that
implements java.lang.AutoCloseable, which
includes all objects which implement
java.io.Closeable.
• We don’t need to add an extra finally block
for just passing the closing statements of
the resources.
• The resources will be closed as soon as the
try-catch block is executed.
Java Type Annotations
• Java 8 has included two new features repeating
and type annotations in its prior annotations topic.
• In early Java versions, you can apply annotations
only to declarations.
• After releasing of Java SE 8 , annotations can be
applied to any type use.
• It means that annotations can be used anywhere.
For example, if you want to avoid NullPointerException in
your code, you can declare a string variable like this:
@NonNull String str;
Following are the examples of type annotations:
1.@NonNull List<String>
2.List<@NonNull String> str
3.Arrays<@NonNegative Integer> sort
4.@Encrypted File file
5.@Open Connection connection
6.void divideInteger(int a, int b) throws @ZeroDiv
isor ArithmeticException
Repeating Annotations
• Java allows you to repeating annotations in
your source code.
• It is helpful when you want to reuse
annotation for the same class. You can repeat
an annotation anywhere that you would use a
standard annotation.
• For compatibility reasons, repeating
annotations are stored in a container
annotation that is automatically generated by
the Java compiler
In order for the compiler to do this, two
declarations are required in your code.
➢Declare a repeatable annotation type
➢Declare the containing annotation type
1) Declare a repeatable annotation type
Declaring of repeatable annotation type must be marked
with the @Repeatable meta-annotation.
In the following example, we have defined a custom
@Game repeatable annotation type.
@Repeatable(Games.class)
@interfaceGame{
String name();
String day();
}
2) Declare the containing annotation type
Containing annotation type must have a value element
with an array type.
The component type of the array type must be the
repeatable annotation type.
In the following example, we are declaring Games
containing annotation type:
@interfaceGames{
Game[] value();
}
Repeating Annotations Example
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
// Declaring repeatable annotation type
@Repeatable(Games.class)
@Retention(RetentionPolicy.RUNTIME)
@interface Game {
String name();
String day();
}
// Declaring container for repeatable annotation type
@Retention(RetentionPolicy.RUNTIME)
@interface Games {
Game[] value();
}
// Repeating annotation
@Game(name = "Cricket", day = "Sunday")
@Game(name = "Hockey", day = "Friday")
@Game(name = "Football", day = "Saturday")
public class RepeatingAnnotationsExample {
public static void main(String[] args) {
// Getting annotation by type into an array
Game[] games =
RepeatingAnnotationsExample.class.getAnnotationsByType(Game.class);
for (Game game : games) { // Iterating values
System.out.println(game.name() + " on " +
game.day());
}
}
}
Output
Cricket on Sunday
Hockey on Friday
Football on Saturday
Object Oriented Programming with Java
(Subject Code: BCS-403)
Unit 3
Lecture 25
Lecture 25
• Java Module System
• Diamond Syntax with 08 Inner Anonymous
Class
Java 9 Module System
Java Module System is a major change in
Java 9 version.
Java added this feature to collect Java
packages and code into a single unit called
module.
• In earlier versions of Java, there was no concept
of module to create modular Java applications,
that why size of application increased and difficult
to move around.
• Even JDK itself was too heavy in size, in Java 8,
rt.jar file size is around 64MB.
• To deal with situation, Java 9 restructured JDK
into set of modules so that we can use only
required module for our project.
module system includes various tools and options
• It Includes various options to the Java tools javac, jlink
and java where we can specify module paths that
locates to the location of module.
• Modular JAR file is introduced. This JAR contains
module-info.class file in its root folder.
• JMOD format is introduced, which is a packaging
format similar to JAR except it can include native code
and configuration files.
• The JDK and JRE both are reconstructed to
accommodate modules. It improves performance,
security and maintainability.
• Java defines a new URI scheme for naming modules,
classes and resources.
Java 9 Module
Module is a collection of Java programs or software.
To describe a module, a Java file module-info.java
is required.
This file also known as module descriptor and
defines the following
➢ Module name
➢ What does it export
➢ What does it require
Module Name
It is a name of module and should follow the
reverse-domain-pattern. Like we name packages,
e.g. com.javatpoint.
How to create Java module
Creating Java module required the following
steps.
➢Create a directory structure
➢Create a module declarator
➢Java source code
Create a Directory Structure
Output:
Hello from the Java module
Diamond Operator
• Diamond operator was introduced in Java 7 as a
new feature.
• The main purpose of the diamond operator is to
simplify the use of generics when creating an
object.
• It avoids unchecked warnings in a program and
makes the program more readable.
• The diamond operator could not be used with
Anonymous inner classes in JDK 7.
• In JDK 9, it can be used with the anonymous
class as well to simplify code and improves
readability.
• Before JDK 7, we have to create an object with
Generic type on both side of the expression like
List<String> KIET= new ArrayList<String>();
Unit 3
Lecture 26
Lecture 26
• Local Variable Type Inference
• Switch Expressions
• Yield Keyword
Local Variable Type Inference
• Local variable type inference is a feature in
Java 10 that allows the developer to skip
the type declaration associated with local
variables (those defined inside method
definitions, initialization blocks, for-loops,
and other blocks like if-else), and the type is
inferred by the JDK.
• It will, then, be the job of the compiler to
figure out the datatype of the variable.
// declaration using LVTI
// Java code for local variable
import java.util.ArrayList;
import java.util.List;
class A {
public static void main(String ap[])
{
var data = new ArrayList<>();
}
}
Cases where you can declare variables using LVTI
// block using LVTI in Java 10
class A {
static
{
var x = "Hi there";
System.out.println(x)'
}
public static void main(String[] ax)
{
}
}
As iteration variable in enhanced for-loop
public class MyMain {
public static void main(String[] args) {
int[] arr ={ 1, 2, 3 };
for (var x : arr)
System.out.println(x);
}
}
As looping index in for-loop
public class MyMain {
public static void main(String[] args)
{
int[] arr = { 1, 2, 3 };
for (var x = 0; x < 3; x++)
System.out.println(arr[x]);
}
}
As a return value from another method
public class MyMain {
int ret()
{
return 1;
}
public static void main(String a[])
{
var x = new MyMain().ret();
System.out.println(x);
}
}
As a return value in a method
public class MyMain {
int ret()
{
var x = 1;
return x;
}
public static void main(String a[])
{
System.out.println(new MyMain().ret());
}
}
There are cases where declaration of local variables
using the keyword ‘var’ produces an error.
1. Not permitted in class fields
class A {
var x;
}
2. Not permitted for uninitialized local variables
3. Not allowed as parameter for any methods
4. Not permitted in method return type.
public var show()
5. Not permitted with variable initialized with ‘NULL’
Switch Expressions
• Until Java 7 only integers could be used in switch
case and this had been the standard for a long
time.
• In Java 8 strings & enum were introduced in
case values and switch statements started to
evolve.
public class MyMain {
public static void main(String a[])
{
String day = "Tuesday";
switch (day) {
case "Monday":
System.out.println("Week day");
break;
case "Tuesday":
.
.
break;
case "Friday":
System.out.println("Week day");
break;
case "Saturday":
System.out.println("Weekend");
break;
case "Sunday":
System.out.println("Weekend");
break;
default:
System.out.println("Unknown");
}}}
public class MyMain {
public static void main(String a[])
{
enum DAYS {
MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
}
DAYS days = DAYS.MONDAY;
switch (days) {
case MONDAY:
System.out.println("Weekdays");
..
break;
case FRIDAY:
System.out.println("Weekdays");
break;
case SATURDAY:
System.out.println("Weekends");
break;
case SUNDAY:
System.out.println("Weekends");
break;
default:
System.out.println("Unknown");
}}}
Java 12 : Switch Statement
• Java 12 further enhanced the switch statement and
introduced switch expressions as a preview feature.
It introduced a flurry of new features:
• We can return values from a switch block and hence
switch statements became switch expressions
• We can have multiple values in a case label
• We can return value from a switch expression through
the arrow operator or through the “break” keyword
switch expressions Example
public class MyMain {
public static void main(String[] args) {
String day = "Wednesday";
String category = getDayCategory(day);
System.out.println(day + " is a " + category + ".");
}
public static String getDayCategory(String day) {
return switch (day) {
case "Monday", "Tuesday", "Wednesday", "Thursday",
"Friday" -> "Weekday";
case "Saturday", "Sunday" -> "Weekend";
default -> "Unknown";
};
}}
Output:Wednesday is a Weekday.
Return value through break keyword
return switch (day) {
case "Monday":
break "Weekday";
case "Tuesday":
break "Weekday";
case "Friday":
break "Weekday";
case "Saturday":
break "Weekend";
case "Sunday":
break "Weekend";
default:
break "Unknown";
};
The word break was replaced by “yield” later in Java 13.
return switch (day) {
case "Monday":
yield "Weekday";
case "Tuesday":
yield "Weekday";
case "Sunday":
yield "Weekend";
default:
yield "Unknown";
};
Return value through arrow operator :
return switch (day)
{ case "Monday"-> "Week day";
case "Tuesday"-> "Week day";
case "Wednesday"->"Week day";
case "Thursday"->"Week day";
case "Friday"->"Week day";
case "Saturday"-> "Weekend";
case "Sunday"-> "Weekend";
default->"Unknown";
};
Multiple case labels :
return switch (day) {
case
"Monday","Tuesday","Wednesday","Thursday","Friday"
-> "Week day";
case "Saturday", "Sunday" -> "Weekend";
default->"Unknown";
};
Java 17 : Switch Statement / Expression :
Java 17 LTS is the latest long-term support release
for the Java SE platform and it was released on
September 15, 2021.
Switch expression features
➢ Pattern matching
➢ Gaurded pattern
➢ Null cases
Pattern Matching :
We can pass objects in switch condition and this
object can be checked for different types in switch
case labels.
Unit 3
Lecture 27
Lecture 26
• Text Blocks
• Records
• Sealed Classes
Text Blocks in Java 15
• In earlier releases of the JDK, embedding multi-
line code snippets required a tangled mess of
explicit line terminators, string concatenations,
and delimiters.
• Text blocks eliminate most of these obstructions,
allowing you to embed code snippets and text
sequences more or less as-is.
• A text block is an alternative form of Java string
representation that can be used anywhere a
traditional double-quoted string literal can be
used.
Text blocks begin with a “”” (3 double-quote marks)
observed through non-obligatory whitespaces and a
newline.