UNIT-3 JAVA NEW FEATURES
OOP WITH JAVA(BCS 403)
JAVA LAMBDA EXPRESSIONS
• Lambda expression 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.
• 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.
FUNCTIONAL INTERFACE
• Lambda expression provides implementation
of functional interface.
• An interface which has only one abstract method is
called functional interface.
• Java provides an annotation @FunctionalInterface,
which is used to declare an interface as functional
interface.
WHY USE LAMBDA EXPRESSION?
• To provide the implementation of Functional interface.
• Less coding.
JAVA LAMBDA EXPRESSION SYNTAX
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.
JAVA LAMBDA EXPRESSION EXAMPLE: MULTIPLE
PARAMETERS
interface Addable {
int add(int a,int b); // Multiple parameters with data type
in lambda expression
} Addable ad2=(int a,int b)->(a+b);
public class LambdaExpressionExample System.out.println(ad2.add(100,200));
{ }
public static void main(String[] args) { }
// Multiple parameters in lambda expres
sion
Addable ad1=(a,b)->(a+b);
System.out.println(ad1.add(10,20));
Functional Interface
Functional Interface is also known as Single Abstract Method Interfaces or
SAM Interfaces. It is a new feature in Java, which helps to achieve functional
programming approach.
@FunctionalInterface
interface sayable{
void say(String msg);
}
public class FunctionalInterfaceExample implements sayable{
public void say(String msg){
System.out.println(msg);
}
public static void main(String[] args) {
FunctionalInterfaceExample fie = new FunctionalInterfaceExample();
fie.say("Hello there");
}
}
Java Method References
Java provides a new feature called method reference in
Java 8. Method reference is used to refer method of
functional interface. It is compact and easy form of
lambda expression. Each time when you are using lambda
expression to just referring a method, you can replace
your lambda expression with method reference.
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
Syntax: ContainingClass::staticMethodName
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
Syntax: containingObject::instanceMethodName
Runnable interface contains only one abstract method. So, we can
use it as functional interface.
public class InstanceMethodReference2
{
public void printnMsg()
{
System.out.println("Hello, this is instance method");
}
public static void main(String[] args) {
Thread t2=new Thread(new InstanceMethodReference2()::printnMsg);
t2.start();
}
}
3) Reference to a Constructor
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");
}
}
STREAM API
Stream provides following features:
• 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.
• Stream is lazy and evaluates code only when required.
• 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.
• Stream reduce the length of code.
STREAM API
These operations transform a stream into another stream.They are lazy
and do not produce a result until a terminal operation is called. It also
called Intermediate Operations.
Examples.
• filter(): Filters elements based on a predicate.
• map():Transforms elements using a function.
• sorted(): Sorts elements in a stream.
• distinct(): Removes duplicate elements
• limit()
• skip()
STREAM API
These operations produce a result or side effect.They consume the stream
and cannot be used again. It also called terminal operation. Examples-
• forEach(): Performs an action on each element.
• collect(): Gathers elements into a collection.
• reduce(): Combines elements into a single value.
• count(): Returns the number of elements.
• min()/max(): Returns the minimum/maximum element.
• anyMatch()/allMatch(): Checks if any or all elements match a predicate.
import java.util.*;
import java.util.stream.Collectors;
class Product{
int id;
String name;
float price;
public Product(int id, String name, float price) {
this.id = id;
this.name = name;
this.price = price;
}
}
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));
List<Float> productPriceList2 =productsList.stream()
.filter(p -> p.price > 30000) // filtering data
.map(p->p.price) // fetching price
.collect(Collectors.toList()); // collecting as list
System.out.println(productPriceList2);
}
}
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 Static Methods
Static methods are used to define utility methods.
interface Sayable{ Java Default Methods
// Default method
default void say(){
System.out.println("Hello, this is default method");
}
// Abstract method
void sayMore(String msg);
}
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
}
}
Java Static Methods
interface Sayable{
// default method
default void say(){
System.out.println("Hello, this is default method");
}
// Abstract method
void sayMore(String msg);
// static method
static void sayLouder(String msg)
{
System.out.println(msg);
}
}
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
Sayable.sayLouder("Helloooo..."); // calling static method
}
}
Java Base64 Encode and Decode
Java provides a class Base64 to deal with encryption.You
can encrypt and decrypt your data by using provided
methods.You need to import java.util.Base64 in your
source file to use its methods.
This class provides three different encoders and
decoders to encrypt information at each level.
Nested Classes of Base64
Class Description
Base64.Decoder This class implements a decoder for decoding byte data
using the Base64 encoding scheme as specified in RFC
4648 and RFC 2045.
Base64.Encoder This class implements an encoder for encoding byte data
using the Base64 encoding scheme as specified in RFC
4648 and RFC 2045.
import java.util.Base64;
publicclass Base64BasicEncryptionExample {
publicstaticvoid main(String[] args) {
// Getting encoder
Base64.Encoder encoder = Base64.getEncoder();
// Creating byte array
bytebyteArr[] = {1,2};
// encoding byte array
bytebyteArr2[] = encoder.encode(byteArr);
System.out.println("Encoded byte array: "+byteArr2);
bytebyteArr3[] = newbyte[5]; // Make sure it has enough size to store copied bytes
intx = encoder.encode(byteArr,byteArr3); // Returns number of bytes written
System.out.println("Encoded byte array written to another array: "+byteArr3);
System.out.println("Number of bytes written: "+x);
// Encoding string
String str = encoder.encodeToString("JavaTpoint".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);
}
}
Java 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.
This method takes a single parameter which is a
functional interface. So, you can pass lambda
expression as an argument.
forEach() Signature in Iterable Interface
Syntax: default void forEach(Consumer<super T>action)
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("Hockey");
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);
}
}
Java Annotation
Java Annotation is a tag that represents the metadata i.e. attached
with class, interface, methods or fields to indicate some additional
information which can be used by java compiler and JVM.
Annotations in Java are used to provide additional information, so it
is an alternative option for XML and Java marker interfaces.
Built-In Java Annotations used in Java code
@Override
@SuppressWarnings
@Deprecated
Java Type Annotations
After releasing of Java SE 8 , annotations can be applied to any type use. It means that
annotations can be used anywhere you use a type. 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:
@NonNull List<String>
List<@NonNull String> str
Arrays<@NonNegative Integer> sort
@Encrypted File file
@Open Connection connection
void divideInteger(int a, int b) throws @ZeroDivisor ArithmeticException
Java Repeating Annotations
In Java 8 release, 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.
1. Declare a repeatable annotation type.
@Repeatable(Games.class)
@interfaceGame
{
String name();
String day();
}
2. Declare the containing annotation type. @interfaceGames{
Game[] value();
}
// Importing required packages for repeating annotation
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
// Declaring repeatable annotation type
@Repeatable(Games.class)
@interfaceGame{
String name();
String day();
}
// Declaring container for repeatable annotation type
@Retention(RetentionPolicy.RUNTIME)
@interfaceGames{
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[] game = RepeatingAnnotationsExample.class.getAnnotationsByType(Game.class);
for (Gamegame2 : game) { // Iterating values
System.out.println(game2.name()+" on "+game2.day());
}
}
}
JAVA MODULE SYSTEM
The module system includes various tools and options that are given below.
➢ 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 softwares. 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
Java 9 Anonymous Inner Classes
Data types that can be written in Java program like int, String etc are called denotable
types. Java 9 compiler is enough smart and now can infer type.
Note: This feature is included to Java 9, to add type inference in anonymous inner
classes.
Diamond operator was introduced as a new feature in java SE 7. The purpose of diamond
operator is to avoid redundant code by leaving the generic type in the right side of the
expression.
SWITCH EXPRESSION
Using switch expressions, we can return a value and we can use them within statements
like other expressions. We can use case L -> label to return a value or using yield, we
can return a value from a switch expression.
YIELD KEYWORD
Switch Expression Using "case L:" Statements and the yield Statement
In java, we can get a value from switch expression and switch statement using yield
statement. yield statement returns the value and finishes the switch execution.
Syntax
case label1, label2, ..., labeln -> expression; yield value;
case label1:
case labeln: expression;
yield value;
TEXT BLOCK
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. The
reason for this is that text blocks are primarily designed to support multi-line strings,
and requiring the initial line terminator simplifies the indentation handling rules (see
the section below, Incidental White Space).
class HelloWorld
{
public static void main(String[] args)
{
String s= “””Hello %s your salary is %d”””
;formatted(...args: “Damini”, 2000);
System.out.println(s); }}
SEALED CLASS
In Java, we have the concept of abstract classes. It is mandatory to inherit from these
classes since objects of these classes cannot be instantiated.
On the other hand, there is a concept of a final class in Java, which cannot be inherited
or extended by any other class.
What if we want to restrict the number of classes that can inherit from a particular
class? The answer is sealed class.
So, a sealed class is a technique that limits the number of classes that can inherit the
given class.
This means that only the classes designated by the programmer can inherit from that
particular class, thereby restricting access to it.
When a class is declared sealed, the programmer must specify the list of classes that
can inherit it. The concept of sealed classes in Java was introduced in Java 15.
Steps to Create a Sealed Class
•Define the class that you want to make a seal.
•Add the “sealed” keyword to the class and specify which classes are permitted to inherit it by using the
“permits” keyword.
sealed class A permits B{
void show() {
}
}
final class B extends A
{
}
public class C
{
}
public class sealdclass {
public static void main(String args[]){}
}
}
RECORDS
In Java, a record is a special type of class declaration aimed at reducing the boilerplate
code. Java records were introduced with the intention to be used as a fast way to create
data carrier classes, i.e. the classes whose objective is to simply contain data and carry it
between modules, also known as POJOs (Plain Old Java Objects) and DTOs (Data
Transfer Objects).
EXAMPLE:
Consider a simple class Employee, whose objective is to contain an
employee’s data such as its ID and name and act as a data carrier to be
transferred across modules. To create such a simple class, you’d need to
define its constructor, getter, and setter methods, and if you want to use the
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().
// Java Program to Illustrate Record's functionalities
// Main class
class GFG {
// Main driver method
public static void main(String args[]) {
// Creating object with default constructor
Employee e1 = new Employee(1001, "Derok", "Dranf");
// auto generated getter methods
System.out.println(e1.id() + " " + e1.firstName()
+ " " + e1.lastName());
// Auto-generated toString() method
System.out.println(e1.toString());
// Creating object with parameterized constructor
Employee e2 = new Employee(1002, "Seren");
// Using instance methods
e2.getFullName();
// Using static methods
System.out.println("Employee " + e2.id() + " Token =
+ e2.generateEmployeeToken());
// Using the equals() method
System.out.print("Is e1 equal to e2: “ + e1.equals(e2));
}
}
Some more Properties of Records
You can use nested classes and interfaces inside a record.
You can have nested records too, which will implicitly be static.
A record can implement interfaces.
You can create a generic record class.
It is possible to use local record classes (since Java SE 15).
Records are serializable.