0% found this document useful (0 votes)
39 views35 pages

OOOpsunit 3

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)
39 views35 pages

OOOpsunit 3

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/ 35

JAVA NEW FEATURE

Functional Interface in Java


A functional interface in Java is an interface that contains exactly one abstract method. It
can have any number of default or static methods, but only one abstract method.
Functional interfaces are used extensively in lambda expressions, method references, and
streams API.
How to Declare a Functional Interface
You can use the @FunctionalInterface annotation (optional but recommended):

@FunctionalInterface

interface MyFunctionalInterface {

void execute(); // only one abstract method

}
Built-in Functional Interfaces (from java.util.function package)
Interface Abstract Method Description
Function<T,R> R apply(T t) Takes one input and returns a result
Consumer<T> void accept(T t) Takes one input, returns nothing
Supplier<T> T get() Takes no input, returns a result
Predicate<T> boolean test(T t) Returns a boolean based on a test condition
BiFunction<T,U,R> R apply(T t, U u) Two inputs, one result

Example: Functional Interface with Static Method

// 1. Define the functional interface

@FunctionalInterface

interface Printer {

void print(String message); // Only one abstract method

// Static method in interface

static void printInfo() {

System.out.println("Printer is a functional interface with one abstract method.");

}
// 2. Implement the interface using a regular class

class ConsolePrinter implements Printer {

public void print(String message) {

System.out.println("Printing: " + message);

// 3. Main class to use the interface

public class Main {

public static void main(String[] args) {

// Calling the static method of the interface

Printer.printInfo();

// Creating an instance of the class that implements the interface

Printer printer = new ConsolePrinter();

printer.print("Hello from functional interface!");

}
Output:

Printer is a functional interface with one abstract method.

Printing: Hello from functional interface!

Functional Interface with Default Method

// Define the functional interface

@FunctionalInterface

interface Greeter {

// One abstract method (required)


void greet(String name);

// One default method (optional)

default void sayGoodbye() {

System.out.println("Goodbye!");

// Implement the interface with a regular class

class EnglishGreeter implements Greeter {

@Override

public void greet(String name) {

System.out.println("Hello, " + name + "!");

// Main class to run the program

public class Main {

public static void main(String[] args) {

// Create an instance of the implementing class

Greeter greeter = new EnglishGreeter();

// Call the abstract method

greeter.greet("Alice");

// Call the default method

greeter.sayGoodbye();

Output:

Hello, Alice!
Goodbye!

Lambda Expression in Java

A lambda expression is a concise way to represent an instance of a functional interface. It’s


a major feature introduced in Java 8 that enables functional programming in Java.

Syntax of Lambda Expression

(parameters) -> expression

// or

(parameters) -> { statements }

Simple Example

@FunctionalInterface

interface Greeting {

void sayHello(String name);

public class Main {

public static void main(String[] args) {

// Lambda expression to implement the Greeting interface

Greeting greet = (name) -> System.out.println("Hello, " + name + "!");

// Call the method

greet.sayHello("Alice");

Output:

Hello, Alice!

Why Use Lambda Expressions?


 Less boilerplate code

 More readable and expressive

 Enables functional-style operations (especially in collections/streams)

Built-in Example: Using Predicate with Lambda

import java.util.function.Predicate;

public class Main {

public static void main(String[] args) {

Predicate<String> isLong = str -> str.length() > 5;

System.out.println(isLong.test("Hello")); // false

System.out.println(isLong.test("Functional")); // true

Output:

false

true

lambda expression with a custom functional interface

@FunctionalInterface

interface Greeting {

void sayHello();

public class Main {

public static void main(String[] args) {


Greeting greet = () -> System.out.println("Hello, ");

greet.sayHello();

OUtPUT

Hello,

Program: Addition Using Functional Interface + Lambda

@FunctionalInterface

interface Adder {

int add(int a, int b);

public class Main {

public static void main(String[] args) {

// Lambda expression to implement the add method

Adder adder = (a, b) -> a + b;

// Perform addition

int result = adder.add(10, 20);

System.out.println("Sum: " + result);

OUTPUT:

Sum: 30

Method Reference in Java


Method Reference is a shorthand syntax for a lambda expression that simply calls an
existing method. It improves code readability and conciseness.
Introduced in Java 8, method references work only with functional interfaces (i.e.,
interfaces with a single abstract method).
Syntax
ClassName::methodName
Types of Method References

Java supports four types of method references:

Type Syntax Example Equivalent Lambda


1. Reference
(a, b) ->
to a static ClassName::staticMethod Math::max
Math.max(a, b)
method
2. Reference
to an
instance () ->
instance::instanceMethod str::toUpperCase
method of a str.toUpperCase()
particular
object
3. Reference
to an
instance
method of s ->
ClassName::instanceMethod String::toLowerCase
an arbitrary s.toLowerCase()
object of a
particular
type
4. Reference
() -> new
to a ClassName::new ArrayList::new
ArrayList<>()
constructor

Code Examples

1. Static Method Reference

@FunctionalInterface

interface Adder {

int add(int a, int b);

public class Main {

public static int sum(int a, int b) {

return a + b;

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

Adder adder = Main::sum; // Method reference to static method

System.out.println("Sum: " + adder.add(5, 10));

2. Instance Method Reference (Specific Object)

@FunctionalInterface

interface Printer {

void print();

public class Main {

public void sayHello() {

System.out.println("Hello!");

public static void main(String[] args) {

Main obj = new Main();

Printer printer = obj::sayHello;

printer.print();

3. Instance Method Reference (Arbitrary Object)

import java.util.function.Function;
public class Main {

public static void main(String[] args) {

Function<String, String> toLower = String::toLowerCase;

System.out.println(toLower.apply("JAVA")); // Output: java

4. Constructor Reference

import java.util.function.Supplier;

import java.util.ArrayList;

public class Main {

public static void main(String[] args) {

Supplier<ArrayList<String>> listSupplier = ArrayList::new;

ArrayList<String> list = listSupplier.get();

list.add("Java");

System.out.println(list); // Output: [Java]

Stream API in Java


The Stream API in Java (introduced in Java 8) provides a modern, declarative way to process
collections (like List, Set, etc.). Instead of writing complex for loops, you can chain stream
operations to filter, map, sort, and collect data.

Key Concepts

 Stream: A sequence of elements supporting sequential and parallel operations.


 Intermediate operations: Return a stream (e.g., filter(), map(), sorted()).
 Terminal operations: Return a result or produce a side-effect (e.g., collect(),
forEach(), count()).
General Syntax of Stream API in Java
The general syntax of the Stream API follows a pipeline pattern:
collection.stream() // 1. Create a stream from a collection

.intermediateOperation1() // 2. Optional intermediate operations

.intermediateOperation2()

...

.terminalOperation(); // 3. Terminal operation (required)

Components Explained

Step Description Examples


1. Stream Creation Start with a collection or array list.stream()
2. Intermediate Transform or filter data (returns a filter(), map(), sorted(),
Operations stream) distinct()
3. Terminal Produces a result or side effect collect(), forEach(),
Operation (ends the stream) count(), reduce()

Example: Stream API with List


import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class Main {


public static void main(String[] args) {
List<String> names = Arrays.asList("John", "Jane", "Jake", "Jill", "Tom", "Tim");

// Filter names starting with 'J', convert to uppercase, sort, and collect to list
List<String> result = names.stream()
.filter(name -> name.startsWith("J")) // Intermediate
.map(String::toUpperCase) // Intermediate
.sorted() // Intermediate
.collect(Collectors.toList()); // Terminal

System.out.println(result);
}
}
Output:
[JAKE, JANE, JILL, JOHN]

Common Stream Methods


Method Description
filter(Predicate) Filters elements
map(Function) Transforms elements
sorted() Sorts elements
distinct() Removes duplicates
limit(n) Limits to first n elements
skip(n) Skips first n elements
collect(Collectors.toList()) Converts stream to a list
forEach(Consumer) Performs an action on each element
count() Counts elements

General Syntax Stream API Program


import java.util.*;
import java.util.stream.*;

public class Main {


public static void main(String[] args) {
// Step 1: Create a collection (List, Set, etc.)
List<DataType> list = Arrays.asList(value1, value2, value3, ...);

// Step 2: Process the data using Stream API


ResultType result = list.stream() // create stream
.filter(element -> condition) // optional: filter
.map(element -> transformation) // optional: map
.sorted() // optional: sort
.distinct() // optional: remove duplicates
.collect(Collectors.toList()); // or another terminal operation

// Step 3: Display the result


System.out.println(result);
}
}
Example of Working Version (Based on the Template)
import java.util.*;
import java.util.stream.*;

public class Main {


public static void main(String[] args) {
List<Integer> list = Arrays.asList(5, 10, 15, 20, 10, 5);

List<Integer> result = list.stream()


.filter(n -> n > 10)
.distinct()
.sorted()
.collect(Collectors.toList());

System.out.println(result);
}
}
OUTPUT:
[15, 20]

Example 1: Count Strings Starting with 'A'


import java.util.*;
import java.util.stream.*;

public class Main {


public static void main(String[] args) {
List<String> names = Arrays.asList("Alice", "Bob", "Andrew", "Angela", "Brian");

long count = names.stream()


.filter(name -> name.startsWith("A"))
.count();

System.out.println("Names starting with 'A': " + count);


}
}
Output:
Names starting with 'A': 3

Example 2: Square Each Number and Collect Results


import java.util.*;
import java.util.stream.*;

public class Main {


public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);

List<Integer> squares = numbers.stream()


.map(n -> n * n)
.collect(Collectors.toList());

System.out.println("Squares: " + squares);


}
}
Output:
Squares: [1, 4, 9, 16, 25]

Example 3: Filter and Print Even Numbers Using forEach


import java.util.*;
import java.util.stream.*;

public class Main {


public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(10, 15, 20, 25, 30);

System.out.print("Even numbers: ");


numbers.stream()
.filter(n -> n % 2 == 0)
.forEach(n -> System.out.print(n + " "));
}
}
OUTPUT:
Even numbers: 10 20 30

Program: Using reduce() to Sum All Elements

import java.util.*;
import java.util.stream.*;

public class Main {


public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(5, 10, 15, 20);

// Using reduce to calculate the sum


int sum = numbers.stream()
.reduce(0, (a, b) -> a + b);

System.out.println("Sum of all numbers: " + sum);


}
}
Output:
Sum of all numbers: 50

Grouping Person by City


import java.util.*;
import java.util.stream.*;

class Person {
String name, city;
Person(String name, String city) {
this.name = name;
this.city = city;
}

public String getCity() {


return city;
}

public String getName() {


return name;
}

@Override
public String toString() {
return name; // Simplify output to just names
}
}

public class Main {


public static void main(String[] args) {
List<Person> people = Arrays.asList(
new Person("Alice", "Delhi"),
new Person("Bob", "Mumbai"),
new Person("Charlie", "Delhi")
);

Map<String, List<Person>> groupedByCity = people.stream()


.collect(Collectors.groupingBy(Person::getCity));

System.out.println(groupedByCity);
}
}
Output:
{Mumbai=[Bob], Delhi=[Alice, Charlie]}

Example: Group Students by Grade

import java.util.*;
import java.util.stream.*;

class Student {
String name;
String grade;

Student(String name, String grade) {


this.name = name;
this.grade = grade;
}

public String getGrade() {


return grade;
}

public String getName() {


return name;
}

@Override
public String toString() {
return name; // Show only student names in output
}
}

public class Main {


public static void main(String[] args) {
List<Student> students = Arrays.asList(
new Student("John", "A"),
new Student("Emma", "B"),
new Student("Sophia", "A"),
new Student("Mike", "C"),
new Student("Chris", "B")
);

Map<String, List<Student>> groupedByGrade = students.stream()


.collect(Collectors.groupingBy(Student::getGrade));

System.out.println(groupedByGrade);
}
}
Output:
{A=[John, Sophia], B=[Emma, Chris], C=[Mike]}

Default Methods

 Used to provide a default implementation inside an interface.


 Helps in adding new methods to interfaces without breaking existing code.

Static Methods

 Belong to the interface itself, not to instances.


 Can be called using the interface name.

Example: Interface with Default and Static Methods


interface MyInterface {
// Default method
default void showMessage() {
System.out.println("Default method in interface");
}

// Static method
static void showStatic() {
System.out.println("Static method in interface");
}
}

// Implementing the interface


public class Main implements MyInterface {
public static void main(String[] args) {
Main obj = new Main();

// Calling default method using object


obj.showMessage();

// Calling static method using interface name


MyInterface.showStatic();
}
}
Output:
Default method in interface
Static method in interface

What is Base64 Encoding and Decoding in Java?


Base64 is a binary-to-text encoding scheme that encodes binary data (like bytes or files) into
an ASCII string using 64 characters. It's commonly used to safely transmit data over media
that are designed to handle text (like JSON, XML, HTTP).

Why Use Base64?

 To encode binary data (e.g., images, files) for transmission in text-based protocols.
 To embed binary content (like images) directly into HTML, XML, or JSON.
 For basic authentication in HTTP headers.

Java Base64 Support

Since Java 8, the class java.util.Base64 provides built-in encoding and decoding.

Common Methods in Base64 Class

Method Description
Base64.getEncoder() Returns a standard Base64 encoder
Base64.getDecoder() Returns a standard Base64 decoder
Base64.getUrlEncoder() Returns a URL-safe encoder (replaces + and /)
Base64.getMimeEncoder() Returns a MIME-compliant encoder (adds line breaks)
encodeToString(byte[]) Encodes bytes into a Base64 string
decode(String) Decodes a Base64 string into bytes

EXAMPLE

import java.util.Base64;

import java.util.Scanner;

public class Base64EncodeDecodeExample {

public static void main(String[] args) {

Scanner scanner = new Scanner(System.in);

// --- Encoding ---

System.out.print("Enter a string to encode: ");

String input = scanner.nextLine();

String encoded = Base64.getEncoder().encodeToString(input.getBytes());

System.out.println("Encoded: " + encoded);

// --- Decoding ---


System.out.print("Enter a Base64 encoded string to decode: ");

String encodedInput = scanner.nextLine();

try {

byte[] decodedBytes = Base64.getDecoder().decode(encodedInput);

String decoded = new String(decodedBytes);

System.out.println("Decoded: " + decoded);

} catch (IllegalArgumentException e) {

System.out.println("Invalid Base64 input!");

scanner.close();

OutPut:

Enter a string to encode: Btech Section A2 CSE

Encoded: QnRlY2ggU2VjdGlvbiBBMiBDU0U=

Enter a Base64 encoded string to decode: QnRlY2ggU2VjdGlvbiBBMiBDU0U=

Decoded: Btech Section A2 CSE

What is forEach() in Java?


The forEach() method in Java is a default method added in Java 8 to the Iterable and
Stream interfaces.
It is used to iterate over elements in a collection or stream in a more concise and
functional way.

Syntax
collection.forEach(action);
Where action is typically a lambda expression or method reference.
Example with List
import java.util.*;
public class Main {
public static void main(String[] args) {
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");

// Using lambda expression


names.forEach(name -> System.out.println(name));
}
}
Output:
Alice
Bob
Charlie

Example with Method Reference


names.forEach(System.out::println);

Example with Stream


names.stream()
.filter(name -> name.startsWith("A"))
.forEach(System.out::println);

What is try-with-resources in Java?


try-with-resources is a Java feature (introduced in Java 7) that allows you to automatically
close resources (like files, streams, connections) when you're done with them — without
writing explicit finally blocks.
It works with any class that implements the AutoCloseable interface (like FileReader,
BufferedReader, Scanner, Connection, etc.).
Syntax
try (ResourceType resource = new ResourceType(...)) {
// Use the resource
} catch (Exception e) {
// Handle exception
}

Example: Reading a File Using try-with-resources


import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

public class Main {


public static void main(String[] args) {
String filePath = "example.txt"; // Make sure this file exists
try (BufferedReader reader = new BufferedReader(new FileReader(filePath))) {
String line;

while ((line = reader.readLine()) != null) {


System.out.println(line);
}

} catch (IOException e) {
System.out.println("Error reading file: " + e.getMessage());
}
}
}
Output:
Hello, World!
Welcome to try-with-resources in Java.

Benefits of try-with-resources:
Advantage Description
No finally needed Automatically closes resources even if exceptions occur
Cleaner code Less boilerplate, no need to explicitly call close()
Works with many classes Any class that implements AutoCloseable

What are Annotations in Java?


Annotations in Java are metadata (data about data) that provide information to the
compiler, tools, or runtime. They do not directly affect program logic but can influence how
code is processed, especially in frameworks (like Spring, JUnit) or tools (like Lombok).
Common Use Cases

 Compile-time instructions (e.g., @Override)


 Code generation (e.g., Lombok)
 Runtime behavior control (e.g., Spring, JPA)
 Documentation (e.g., @Deprecated)

Built-in Annotations in Java


Annotation Description
@Override Indicates a method overrides a superclass method
@Deprecated Marks a method/class as outdated
@SuppressWarnings Instructs the compiler to ignore warnings
@FunctionalInterface Ensures the interface has exactly one abstract method
@SafeVarargs Suppresses warnings for generic varargs
@Retention / @Target Used when creating custom annotations
Example 1: @Override
class Animal {
void sound() {
System.out.println("Animal sound");
}
}

class Dog extends Animal {


@Override
void sound() {
System.out.println("Bark");
}
}
Output:
Bark

Example 2: @Deprecated
@Deprecated
void oldMethod() {
System.out.println("This method is deprecated");
}
Example 3: Custom Annotation
import java.lang.annotation.*;
import java.lang.reflect.Method;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@interface MyAnnotation {
String value();
}

public class Main {


@MyAnnotation("This is custom annotation")
public void demo() {
System.out.println("Annotated method");
}

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


Main obj = new Main();
Method method = obj.getClass().getMethod("demo");

if (method.isAnnotationPresent(MyAnnotation.class)) {
MyAnnotation annotation = method.getAnnotation(MyAnnotation.class);
System.out.println("Annotation value: " + annotation.value());
}
obj.demo(); // call the method too
}
}
Output:
Annotation value: This is custom annotation
Annotated method

What is a Type Annotation in Java?


Type annotations were introduced in Java 8 (via JSR 308). They allow annotations to be
applied wherever a type is used, not just on declarations.

Type Annotation (Java 8 and above)

Can be used on:

 Instantiations
 Casts
 implements / extends
 Generic type parameters
 Method return types and parameters

Example of Type Annotations


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

@Target({ElementType.TYPE_USE})
@Retention(RetentionPolicy.RUNTIME)
@interface NonNull {
}

// Using type annotation


public class Main {
public static void main(String[] args) {
@NonNull String name = "Java";
List<@NonNull String> list = new ArrayList<>();

list.add("A");
list.add("B");

for (@NonNull String str : list) {


System.out.println(str);
}
}
}
Output:
A
B
Where Type Annotations Can Be Used
Context Example
Variable declarations @NonNull String name;
Generics List<@NonNull String>
Method return types public @NonNull String getName()
Casts (@NonNull String) obj
Implements clause class MyClass implements @NonNull Runnable

What are Repeating Annotations in Java?


Repeating annotations (introduced in Java 8) allow you to apply the same annotation
multiple times to a single element (e.g., class, method, field).
Why It's Needed
Before Java 8, you couldn't apply the same annotation more than once to the same element.
Now, with repeating annotations, it's possible — with a bit of setup.
How to Create Repeating Annotations
You need two annotations:
1. The main annotation (e.g., @Hint)
2. A container annotation (e.g., @Hints)
import java.lang.annotation.*;
import java.lang.reflect.Method;

// 1. Define repeatable annotation


@Repeatable(Hints.class)
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@interface Hint {
String value();
}

// 2. Define the container annotation


@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@interface Hints {
Hint[] value();
}

// 3. Use the repeating annotation


public class Main {
@Hint("Hint One")
@Hint("Hint Two")
public void show() {
System.out.println("Method with multiple hints");
}

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


Method method = Main.class.getMethod("show");

// Access all annotations of type Hint


Hint[] hints = method.getAnnotationsByType(Hint.class);

for (Hint hint : hints) {


System.out.println("Hint: " + hint.value());
}
}
}
Output:
Hint: Hint One
Hint: Hint Two
What is the Java Module System?
The Java Module System (also known as Project Jigsaw) was introduced in Java 9 to
modularize the Java platform and your own applications.
It brings a structured way to organize large codebases into reliable and encapsulated
components, called modules.
Why Use Java Modules?
Before Java 9:
 Applications used packages and JARs without strong boundaries.
 Everything was accessible, which caused conflicts, security issues, and classpath
hell.
With Java Modules:
 You explicitly define dependencies.
 You control which packages are accessible.
 You hide internal implementations (strong encapsulation).
Core Concepts
Term Description
Module A self-contained unit of code (like a JAR)
module-info.java Special file that declares module name, dependencies, and exports
Exports Specifies which packages are accessible to other modules
Requires Specifies which modules this one depends on
Benefits of Java Module System
Feature Benefit
Encapsulation Hide internal APIs by default
Reliable dependencies Compiler checks for missing modules
Better tooling IDEs and build tools get better structure
Smaller JDK Enables custom runtimes via jlink
Security Prevents illegal access to internal APIs
Diamond Syntax (<>) with Inner Anonymous Classes in Java
What is Diamond Syntax?
The diamond operator (<>), introduced in Java 7, infers generic types on the right-hand
side of object creation.
Example:
List<String> list = new ArrayList<>(); // diamond syntax

Limitation with Anonymous Inner Classes


You cannot use diamond syntax with anonymous inner classes that use generics, because
the Java compiler can't infer the type for the anonymous subclass.

What is Local Variable Type Inference in Java?


Local Variable Type Inference is a feature introduced in Java 10 that allows you to declare
local variables using the reserved word var instead of explicitly writing the type.

Syntax:

var variableName = expression;

Example:

var name = "Java"; // inferred as String

var number = 10; // inferred as int

var list = new ArrayList<String>(); // inferred as ArrayList<String>

example

import java.util.ArrayList;

import java.util.List;

public class Main {

public static void main(String[] args) {

// Using 'var' for type inference

var name = "Java"; // inferred as String

var number = 42; // inferred as int

var list = new ArrayList<String>(); // inferred as ArrayList<String>


list.add("Hello");

list.add("World");

System.out.println("Name: " + name);

System.out.println("Number: " + number);

for (var item : list) { // 'item' inferred as String

System.out.println(item);

Output:

Name: Java

Number: 42

Hello

World

Switch Expression in Java


Switch expressions, introduced in Java 14 (finalized), are an enhanced version of the
traditional switch statement. They return values and support more concise syntax.
General Syntax of a Switch Expression in Java
Switch expressions were introduced in Java 14 (as a standard feature) to simplify and
improve upon the traditional switch statement. Here's the general syntax:

1. Using Arrow (->) Syntax:

Type variable = switch (expression) {

case value1 -> result1;

case value2, value3 -> result2;

default -> defaultResult;


};

2. Using yield in Block:

Type variable = switch (expression) {

case value1 -> result1;

case value2 -> {

// Some logic

yield result2; // must use yield in block

default -> {

// Default case logic

yield defaultResult;

};

Use yield when you have multi-line blocks.

What is yield in Java?


The yield keyword in Java is used inside a switch expression to return a value from a
case block when that block contains multiple statements.
Why yield?
In a switch expression, when using block syntax (i.e., { ... }), you can't just write a value
directly.
Instead, you must use yield to return the result from that block.
1⃣ Simple Switch Expression

public class SimpleSwitch {

public static void main(String[] args) {

int day = 3;

String dayName = switch (day) {

case 1 -> "Monday";


case 2 -> "Tuesday";

case 3 -> "Wednesday";

default -> "Unknown";

};

System.out.println(dayName); // Output: Wednesday

}
Output:
Wednesday

Switch Expression with Multiple Labels

public class MultiLabelSwitch {

public static void main(String[] args) {

String fruit = "Apple";

String category = switch (fruit) {

case "Apple", "Banana", "Mango" -> "Fruit";

case "Carrot", "Potato" -> "Vegetable";

default -> "Unknown";

};

System.out.println(category); // Output: Fruit

}
Output:
Fruit

Switch Expression with yield Keyword


public class YieldSwitch {

public static void main(String[] args) {

int score = 85;

String grade = switch (score / 10) {

case 10, 9 -> "A";

case 8 -> {

System.out.println("Good performance");

yield "B"; // Use yield to return from block

case 7 -> "C";

default -> "F";

};

System.out.println("Grade: " + grade); // Output: Good performance\nGrade: B

}
Output:
Good performance\nGrade: B
What is a Text Block in Java?
Text Blocks are a feature introduced in Java 13 (as a preview) and standardized in Java 15
to simplify writing multi-line string literals.
They allow you to write strings spanning multiple lines without needing to escape newlines,
quotes, or other characters.

Benefits of Text Blocks:

 Easy to write and read multiline strings (like JSON, HTML, SQL).
 No need for explicit newline (\n) characters.
 Preserves formatting and indentation nicely.
Syntax:

String text = """

This is a

multi-line

text block.

""";

Example:

public class TextBlockExample {

public static void main(String[] args) {

String json = """

"name": "Alice",

"age": 30,

"city": "New York"

""";

System.out.println(json);

}
Output:

"name": "Alice",

"age": 30,

"city": "New York"


}

Notes:

 The opening """ must be followed by a newline.


 The closing """ defines the indentation baseline.
 You can use \s to add a trailing space if needed.
 Supports escape sequences like \n, \t if you want explicit control.

What are Records in Java?


Records were introduced in Java 14 (preview) and standardized in Java 16 as a compact
way to declare classes that are primarily used to store immutable data — like simple data
carriers or DTOs (Data Transfer Objects).

Why Records?

 Boilerplate-free data classes.


 Automatically generate:
o private final fields
o Constructor
o equals(), hashCode()
o toString()
 Immutable by default (fields are final).
 Useful for modeling simple data aggregates.

Syntax:
public record Person(String name, int age) { }, this line is equivalent to:
public final class Person {
private final String name;
private final int age;

public Person(String name, int age) {


this.name = name;
this.age = age;
}

public String name() { return name; }


public int age() { return age; }

// equals(), hashCode(), toString() auto-generated


}

Example

public class RecordExample {

public static void main(String[] args) {

Person p = new Person("Alice", 30);


System.out.println(p.name()); // prints: Alice

System.out.println(p.age()); // prints: 30

System.out.println(p); // prints: Person[name=Alice, age=30]

record Person(String name, int age) {}

Output:
Alice
30
Person[name=Alice, age=30]

Key Points:

Feature Description
Immutable fields All fields are final
Compact syntax Less code compared to regular class
Accessors Auto-generated getter methods named after fields ( name() for name)
No setters Objects are immutable
Can have methods You can add extra methods if needed
What are Sealed Classes in Java?
Sealed classes were introduced in Java 15 (preview) and standardized in Java 17. They
provide a way to restrict which other classes or interfaces may extend or implement
them.

Why use Sealed Classes?

 Control inheritance explicitly.


 Improve security and maintainability.
 Enable exhaustive pattern matching (useful with switch).
 Define a fixed set of subclasses.

Syntax:
public sealed class Shape permits Circle, Rectangle, Square {
// class body
}

 The permits clause lists all allowed subclasses.


 Subclasses must declare themselves as final, sealed, or non-sealed.

When to use?

 When you want a controlled hierarchy, like shapes, events, or domain models.
 When enabling exhaustive checks in switch or instanceof.

Key Points:

Feature Description
Restricts subclasses Only classes listed in permits can extend
Subclasses’ modifiers Must be final, sealed, or non-sealed
Supports better pattern matching Because all subclasses are known
Improves design clarity Clear hierarchy and limited extensibility

EXAMPLE

public class Main {

public static void main(String[] args) {

Animal dog = new Dog();

Animal cat = new Cat();

dog.speak(); // Output: Woof!

cat.speak(); // Output: Meow!

// ✅ Sealed class (must NOT be public in online compilers)

sealed class Animal permits Dog, Cat {

public void speak() {

System.out.println("Some animal sound");

}
// ✅ Final subclass 1

final class Dog extends Animal {

@Override

public void speak() {

System.out.println("Woof!");

// ✅ Final subclass 2

final class Cat extends Animal {

@Override

public void speak() {

System.out.println("Meow!");

Output:

Woof!

Meow!

Benefits of Sealed Classes in Java

Sealed classes, introduced in Java 15 (preview) and finalized in Java 17, offer several
powerful benefits for designing safe, controlled, and maintainable class hierarchies.

1. Controlled Inheritance
2. Better Modeling of Domain Logic
3. Enables Exhaustive Pattern Matching
4. Better Maintainability
5. Documentation and Code Clarity
6. Improved IDE Support & Tooling
Benefit Description
Controlled hierarchy Only allowed classes can extend
Better modeling Clear, domain-specific types
Pattern matching support Enables compiler to check all cases
Safer refactoring Prevents accidental subclassing
Clear documentation Shows which classes belong to the hierarchy

You might also like