Oops3
Oops3
Functional Interfaces
→ An Interface that contains exactly one abstract method is known as functional interface.
→ It represents a single action.
→ It can have any number of default, static methods but can contain only one abstract method.
→ It is known as Single Abstract Method Interfaces.
→ It helps in functional programming task
→ interface with an annotation called @FunctionalInterface
→ No need to use the abstract keyword, by default, method defined inside the interface is abstract only.
@FunctionalInterface
interface interface1{
void printm(String msg);
}
public class FInterfaceDemo1 implements interface1 {
public void printm(String msg){
System.out.println(msg);
}
public static void main(String[] args) {
FInterfaceDemo1 f = new FInterfaceDemo1();
f.printm("functional interface demo");
}
}
→ Java provides predefined functional interfaces to deal with functional programming by using lambda
and method references.
→ You can also define your own custom functional interface. Following is the list of functional interface
which are placed in java.util.function package.
Lambda Expressions
Syntax:-
Page 1
Types of Lambda Expression
→ There are three Lambda Expression Parameters are mentioned below:
○ Zero Parameter
○ Single Parameter
○ Multiple Parameters
1. Lambda Expression with Zero parameter
() -> System.out.println("Zero parameter lambda");
2. Lambda Expression with Single parameter
(p) -> System.out.println("One parameter: " + p);
It is not mandatory to use parentheses if the type of that variable can be inferred from the context
3. Lambda Expression with Multiple parameters
(p1, p2) -> System.out.println("Multiple parameters: " + p1 + ", " + p2);
Example Problems
interface HelloWorld {
void sayHello();
}
interface Greeting {
void sayMessage(String message);
}
interface Arithmetic {
int operation(int a, int b);
}
public class Main {
public static void main(String[] args) {
Arithmetic add = (a, b) -> a + b;
Arithmetic multiply = (a, b) -> a * b;
() → 123.45
(n) → (n%2)==0
Page 2
Example of Lambda Expression with one parameter
//functional interface
interface NumericTest{
boolean test(int n);
}
public class LambdaDemo1 {
public static void main(String args[]) {
NumericTest isEven= (n) -> (n%2)==0;
if(isEven.test(10)) System.out.println("10 is even");
if(!isEven.test(9)) System.out.println("9 is not even");
}
}
Important
// Program to print factorial of a number using lambda block
@FunctionalInterface
public interface RecFunction {
Integer recursiveFunction(Integer n);
}
// Lambda Function
Integer Fact = (n)-> {
if (n == 0) return 1;
else return n * factorial.recursiveFunction ( n - 1 );
};
Page 3
2. Reference to an instance method
Stream API
Page 4
Stream Operations
Intermediate- It returns a stream as the output and intermediate operations are only executed once a
terminal operation is invoked on the stream. This is called lazy evaluation. Intermediate operations produce
another stream. Thus, intermediate operations can be used to create a pipeline that performs a sequence of
actions
Terminal- Terminal operations in streams produce the final results of the stream after all intermediate
operations have been applied, marking the end of stream processing. Once a terminal operation is executed,
the stream cannot be further utilized.
1. map()
The map method is used to return a stream consisting of the results of applying the given function to the
elements of this stream.
List number = Arrays.asList(2,3,4,5);
List square = number.stream().map(x->x*x).collect(Collectors.toList());
2. filter()
The filter method is used to select elements as per the Predicate passed as an argument.
List names = Arrays.asList("Reflection","Collection","Stream");
List result = names.stream().filter(s->s.startsWith("S")).collect(Collectors.toList());
3. sorted()
The sorted method is used to sort the stream.
List names = Arrays.asList("Reflection","Collection","Stream");
List result = names.stream().sorted().collect(Collectors.toList());
1. collect()
The collect method is used to return the result of the intermediate operations performed on the stream.
List number = Arrays.asList(2,3,4,5,3);
Set square = number.stream().map(x->x*x).collect(Collectors.toSet());
2. forEach()
The forEach method is used to iterate through every element of the stream.
List number = Arrays.asList(2,3,4,5);
number.stream().map(x->x*x).forEach(y->System.out.println(y));
3. reduce()
The reduce method is used to reduce the elements of a stream to a single value. The reduce method takes a
BinaryOperator as a parameter.
List number = Arrays.asList(2,3,4,5);
int even = number.stream().filter(x->x%2==0).reduce(0,(ans,i)-> ans+i);
Reduction Operations
→ Many times, we need to perform operations where a stream reduces to single resultant value, for
example, maximum, minimum, sum, product, etc.
→ sum(), min(), max(), count() etc. are some examples of reduce operations.
Page 5
Example of Reduction Operations in Stream API
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
Default Methods
Page 6
Static Method in Interface
→ Java 8 has finally added Base64 capabilities to the standard API, via the java.util.Base64 utility class.
→ There are 3 types of encoding and decoding capabilities as standard.
→ All of the classes are related to this are in java.util.base64 package.
○ Base or Simple Type
○ URL Encoding/Decoding
○ MIME Encoding/Decoding
→ In Base64 encoding, the length of an output-encoded String must be a multiple of four. If necessary, the
encoder adds one or two padding characters (=) at the end of the output as needed in order to meet this
requirement.
→ Upon decoding, the decoder discards these extra padding characters.
→ we need to skip the padding of the output. For instance, the resulting String will never be decoded back.
So, we can simply choose to encode without padding.
→ URL encoding is very similar to the basic encoder. Also, it uses the URL and Filename Safe Base64
alphabet.
→ In addition, it does not add any line separation:
→ Decoding happens in much the same way. The getUrlDecoder() utility method returns
a java.util.Base64.Decoder. So, we use it to decode the URL:
Page 7
Example: Base 64 Simple Encoding and Decoding
ForEach Method
→ Introduced in Java 8, the forEach loop provides programmers with a new, concise and interesting way
to iterate over a collection.
→ with forEach, we can iterate over a collection and perform a given action on each element, like any
other Iterator.
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");
System.out.println("- --Iterating by passing lambda expression---");
gamesList.forEach(games -> System.out.println(games));
}
Output:
---Iterating by passing lambda expression---
Football
Cricket
Chess
Hocky
Try-with-resources
→ This feature allows us to declare resources to be used in a try block with the assurance that the
resources will be closed after the execution of that block.
→ The resources declared need to implement the AutoCloseable interface.
→ Simply put, to be auto-closed, a resource has to be both declared and initialized inside the try:
Page 8
Try-with-resources can replace try-catch-finally block.
Type Annotations
→ As of the Java SE 8 release, annotations can also be applied to any type use.
→ This means that annotations can be used anywhere you use a type.
→ Type annotations were created to support improved analysis of Java programs way of ensuring
stronger type checking.
→ For example, if you want to avoid NullPointerException in your code, you can declare a string variable
like this:
@NonNull String str;
Repeating Annotations
→ Declaring of repeatable annotation type must be marked with the @Repeatable meta-annotation.
→ A custom annotations is defined @Game repeatable annotation type.
1. @Repeatable(Games.class)
2. @interfaceGame{
3. String name();
4. String day();
5. }
→ 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.
→ Example- declaring Games containing annotation type:
1. @interfaceGames{
2. Game[] value();
3. }
Page 9
Java Module System
What is a module?
→ Modularity adds a higher level of aggregation above packages. The key new language element is
the module—a uniquely named, reusable group of related packages, as well as resources (such as
images and XML files) and a module descriptor specifying
-module’s name
-module’s dependencies (other modules this module depends on)
-packages it explicitly makes available to other modules
-services it offers
-services it consumes
-to what other modules it allows reflection
1. System Modules – These are modules listed when we run the list-modules command, include Java SE
and JDK modules.
2. Application Modules – These modules are what we usually want to build when we decide to use
Modules. They are named and defined in the compiled module-info.class file included in the assembled
JAR.
3. Automatic Modules – include unofficial modules by adding existing JAR files to the module path. The
name of module will be derived from the name of the JAR. Automatic modules will have full read
access to every other module loaded by the path.
4. Unnamed Module – When a class or JAR is loaded onto the classpath, but not the module path, it’s
automatically added to the unnamed module. It’s a catch-all module to maintain backward compatibility
with previously-written Java code.
Create and use module in Eclipse
Page 10
Diamond Syntax (<>)
→ Diamond syntax, sometimes known as diamond operator, It was added to Java 7 as just a new feature.
→ Purpose of diamond operator is to avoid redundant code by leaving the generic type in the right side of
the expression.
→ The diamond operator makes it easier to employ generics while building an object. By allowing implicit
duplicate parameter type specification
→ Java 9 introduced a new feature that allows us to use diamond operator with anonymous classes.
→ Using the diamond with anonymous classes was not allowed in Java 7.
→ In Java 9, as long as the inferred type is denotable, we can use the diamond operator when we create an
anonymous inner class.
→ Data types that can be written in Java program like int, String etc are called denotable types.
→ In JDK 10 and later, you can declare local variables with non-null initializers with the var identifier,
which can help writing code that is easier to read.
Page 11
LVTI Guidelines
→ Local variable declarations can make code more readable by eliminating redundant information.
→ It can also make code less readable by omitting useful information.
→ Use this feature with judgment; no strict rule exists about when it should and shouldn't be used.
→ Local variable declarations don't exist in isolation
Switch Expressions
→ Java 12 and 13 improved the traditional switch statement and made it more useful.
→ Like all expressions, this expression evaluate to a single value and can be used in statements
→ They contain “ case L ->” labels that eliminate the need for break statements to prevent fall through.
→ Use a yield statement to specify the value of a switch expression
Page 12
Text Blocks
→ 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 can be used anywhere a string literal can be used.
→ Text blocks may be used as a method argument
→ 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.
→ It provide clarity by way of minimizing the Java syntax required to render a string that spans multiple
lines.
→ A text block begins with three double-quote characters followed by a line terminator.
→ You can't put a text block on a single line, nor can the contents of the text block follow the three opening
double-quotes without an intervening line terminator.
→ Reason for this is that text blocks are primarily designed to support multi-line strings, and requiring
initial line terminator simplifies the indentation handling rules
Records
→ Records are immutable data classes that require only the type and name of fields.
→ The record is a new type of class in Java that makes it easy to create immutable data objects.
→ Introduced in Java 14 as an early preview, Java 15 aims to refine a few aspects before becoming an
official product feature.
EXAMPLE-
Consider a class Employee, objective is to contain employee’s data like ID and name and act as a data carrier
to be transferred across modules.
To create such a simple class, you need to define its constructor, getter, and setter methods, if you want to use
object with data structures like HashMap or print the contents of its objects as a string, we would need to
override methods such as equals(), hashCode(), and toString().
Page 13
Properties of Records
Purpose/Advantage of Records
→ purpose of a record is to create a data object or a POJO which is used to carry data in application
program flow.
→ In a multi-tier program, Domain/Model objects store the data captured from the data source and then
these model objects are passed further to the application/UI layer to process the data and vice versa
where UI/Application stores data in data objects and then pass these objects to Data layer to populate
data sources.
→ As these data objects contain a lot of fields, developers are required to write a lot of setter/getter
methods, parameterized constructors, overridden equals methods, and hashcode methods.
→ In such a scenario, record comes to the rescue as it provides most of the boilerplate code and the
developer can focus on required functionalities only.
To declare a record, use the record keyword, followed by the record name and the state description enclosed
in parenthesis.
Page 14
Sealed Classes
→ Sealed classes are a feature introduced in Java 16 and finalized in Java 17.
→ It allow developers to restrict the inheritance hierarchy of a class or interface.
→ They provide more control over how a class can be extended or implemented.
→ A class or an interface can now define which classes can implement or extend it.
→ It is a useful feature for domain modeling and increasing the security of libraries.
→ We can seal classes by applying the same sealed modifier.
→ The permits clause should be defined after any extends or implements clauses
Example-
Shape specifies three permitted subclasses , Circle, Square, Rectangle
Define Circle.java
Define Square.java
Define Rectangle.java
Define FilledRectangle.java
Page 15