[Go to site: main page, start]

0% found this document useful (0 votes)
27 views38 pages

Java 8 Features Overview with Examples

The document provides an overview of Java 8 features, focusing on lambda expressions, functional interfaces, method references, and the Streams API. It explains how these features enhance code readability and efficiency, allowing for functional programming styles. Additionally, it covers the new Date and Time API, the Optional class, and various built-in functional interfaces, illustrating their use with code examples.

Uploaded by

Jha Avinash
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)
27 views38 pages

Java 8 Features Overview with Examples

The document provides an overview of Java 8 features, focusing on lambda expressions, functional interfaces, method references, and the Streams API. It explains how these features enhance code readability and efficiency, allowing for functional programming styles. Additionally, it covers the new Date and Time API, the Optional class, and various built-in functional interfaces, illustrating their use with code examples.

Uploaded by

Jha Avinash
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

Java 8 Features

Step 1: Understanding Lambda Expressions (Functional Interfaces)

Lambda expressions are a key feature of Java 8, allowing us to write concise and readable
code.

1.1 What is a Lambda Expression?

●​ It is an anonymous function (a function without a name).

Syntax:​
java​

(parameters) -> expression
or​
java​

(parameters) -> { statements; }

1.2 Example

Before Java 8 (Using Anonymous Class):

java

import [Link].*;

public class LambdaExample {


public static void main(String[] args) {
List<String> names = [Link]("Avinash", "Chhaya",
"Java");

// Using Anonymous Inner Class


[Link](names, new Comparator<String>() {
@Override
public int compare(String s1, String s2) {
return [Link](s2);
}
});

[Link](names);
}
}
After Java 8 (Using Lambda):

java

import [Link].*;

public class LambdaExample {


public static void main(String[] args) {
List<String> names = [Link]("Avinash", "Chhaya",
"Java");

// Using Lambda Expression


[Link](names, (s1, s2) -> [Link](s2));

[Link](names);
}
}

1.3 Functional Interfaces

A functional interface is an interface with exactly one abstract method.

Example:

java

@FunctionalInterface
interface MyFunction {
int add(int a, int b);
}

public class FunctionalInterfaceExample {


public static void main(String[] args) {
// Lambda Expression
MyFunction sum = (a, b) -> a + b;
[Link]([Link](5, 10)); // Output: 15
}
}

Key functional interfaces in [Link]:

●​ Predicate<T> – Returns boolean (test(T t))


●​ Function<T, R> – Converts T to R (apply(T t))
●​ Consumer<T> – Consumes T (accept(T t))
●​ Supplier<T> – Provides T (get())

Step 2: Method References

Instead of writing a full lambda, we can reference existing methods.

2.1 Syntax
java

ClassName::methodName

Example:

java

import [Link].*;

public class MethodReferenceExample {


public static void main(String[] args) {
List<String> names = [Link]("Java", "Lambda",
"Stream");

// Using Method Reference


[Link]([Link]::println);
}
}

Step 3: Default and Static Methods in Interfaces

Java 8 allows interfaces to have default and static methods.

3.1 Default Method


java

interface Vehicle {
default void honk() {
[Link]("Honking...");
}
}
class Car implements Vehicle {}

public class DefaultMethodExample {


public static void main(String[] args) {
Car car = new Car();
[Link](); // Honking...
}
}

3.2 Static Method


java

interface Utility {
static void printMessage() {
[Link]("Static method in Interface");
}
}

public class StaticMethodExample {


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

Step 4: Streams API

Streams help process collections in a declarative and functional way.

4.1 Example
java

import [Link].*;
import [Link];

public class StreamExample {


public static void main(String[] args) {
List<String> names = [Link]("Avinash", "Java",
"Stream", "Lambda");
// Filter and collect
List<String> filteredNames = [Link]()
.filter(name ->
[Link]("J"))

.collect([Link]());

[Link](filteredNames); // [Java]
}
}

4.2 Important Stream Operations

●​ filter(Predicate<T>)
●​ map(Function<T, R>)
●​ sorted()
●​ forEach(Consumer<T>)
●​ collect([Link]())
●​ reduce()

Example:

java

import [Link].*;
import [Link];

public class StreamMapExample {


public static void main(String[] args) {
List<String> names = [Link]("java", "stream",
"lambda");

List<String> upperCaseNames = [Link]()


.map(String::toUpperCase)

.collect([Link]());

[Link](upperCaseNames); // [JAVA, STREAM,


LAMBDA]
}
}
Step 5: Optional Class

Avoids NullPointerException by providing a wrapper for optional values.

5.1 Example
java

import [Link];

public class OptionalExample {


public static void main(String[] args) {
Optional<String> optionalName = [Link](null);

// Check if value is present


[Link]([Link]("Default Name")); //
Default Name
}
}

Step 6: New Date and Time API ([Link] package)

Replaces [Link] and [Link].

6.1 Example
java

import [Link];
import [Link];
import [Link];

public class DateTimeExample {


public static void main(String[] args) {
LocalDate date = [Link]();
LocalTime time = [Link]();
LocalDateTime dateTime = [Link]();

[Link]("Date: " + date);


[Link]("Time: " + time);
[Link]("DateTime: " + dateTime);
}
}
Step 7: Collectors and Grouping

Collectors help with data aggregation and grouping.

7.1 Example
java

import [Link].*;
import [Link];

class Employee {
String name;
String department;

Employee(String name, String department) {


[Link] = name;
[Link] = department;
}

public String getDepartment() { return department; }


}

public class GroupingByExample {


public static void main(String[] args) {
List<Employee> employees = [Link](
new Employee("Alice", "IT"),
new Employee("Bob", "HR"),
new Employee("Charlie", "IT")
);

// Grouping by department
Map<String, List<Employee>> grouped = [Link]()

.collect([Link](Employee::getDepartment));

[Link](grouped);
}
}
Lambda Expressions in Java 8 (Deep Dive)

Lambda expressions are the foundation of functional programming in Java 8. They help
us write cleaner, more readable, and less verbose code.

1. What is a Lambda Expression?


A lambda expression is an anonymous function that lets us write shorter and more
readable code. It removes the need for boilerplate code when implementing functional
interfaces.

Syntax:

java

(parameters) -> { expression/body }

2. Why Use Lambda Expressions?


1.​ Less Code – No need for anonymous classes.
2.​ Better Readability – More concise.
3.​ Functional Programming – Enables declarative-style coding.
4.​ Parallel Processing – Works well with Streams API.
5.​ Improved Performance – Less memory usage in some cases.

3. Basic Examples
Without Lambda (Before Java 8)
java

interface MyInterface {
void show();
}

class Demo {
public static void main(String[] args) {
MyInterface obj = new MyInterface() {
@Override
public void show() {
[Link]("Hello, Java 8!");
}
};
[Link]();
}
}

With Lambda (Java 8)


java

interface MyInterface {
void show();
}

class Demo {
public static void main(String[] args) {
MyInterface obj = () -> [Link]("Hello, Java
8!");
[Link]();
}
}

🔹 Less code, same functionality!


4. Lambda Syntax Variations
1. No Parameters
java

() -> [Link]("Lambda without parameters!");

2. Single Parameter (No Parentheses)


java

name -> [Link]("Hello, " + name);


3. Multiple Parameters
java

(a, b) -> [Link](a + b);

4. Multiple Statements in Body


java

(int x, int y) -> {


int sum = x + y;
return sum;
};

5. Returning a Value
java

(int x, int y) -> x + y;

🔹 No need for return when there’s a single statement!


5. Functional Interfaces and Lambdas
A functional interface is an interface with only one abstract method.

Example: Functional Interface with Lambda


java

@FunctionalInterface
interface MyFunctionalInterface {
int add(int a, int b);
}

public class LambdaExample {


public static void main(String[] args) {
MyFunctionalInterface sum = (a, b) -> a + b;
[Link]([Link](5, 10)); // Output: 15
}
}
🔹 We directly implemented add() without a class!

6. Using Lambda with Built-in Functional Interfaces

1. Predicate (Returns true or false)


java

import [Link];

public class PredicateExample {


public static void main(String[] args) {
Predicate<Integer> isEven = x -> x % 2 == 0;
[Link]([Link](10)); // true
}
}

2. Consumer (Accepts a value, returns nothing)


java

import [Link];

public class ConsumerExample {


public static void main(String[] args) {
Consumer<String> printName = name ->
[Link]("Hello, " + name);
[Link]("Avinash"); // Hello, Avinash
}
}

3. Function (Takes an input, returns a value)


java

import [Link];

public class FunctionExample {


public static void main(String[] args) {
Function<Integer, Integer> square = x -> x * x;
[Link]([Link](4)); // 16
}
}

4. Supplier (Returns a value, takes nothing)


java

import [Link];

public class SupplierExample {


public static void main(String[] args) {
Supplier<Double> randomNumber = () -> [Link]();
[Link]([Link]()); // Random number
}
}

7. Scope & Closure in Lambdas


Lambdas capture variables from their surrounding scope (like an inner class).

java

public class LambdaScopeExample {


public static void main(String[] args) {
int num = 10; // Effectively final
Runnable r = () -> [Link](num);
[Link]();
}
}

🔹 Captured variables must be effectively final!

8. Lambda Performance Considerations


✅ When to Use Lambdas?
✔ When you need concise, readable code​
✔ When working with functional interfaces​
✔ In Streams, Collections, and Parallel Processing
❌ When to Avoid Lambdas?
✘ If the logic is too complex → Use normal methods​
✘ If readability is compromised​
✘ If performance is a concern (Anonymous classes may be faster)

9. Common Lambda Interview Questions


1.​ What is a Lambda Expression?
2.​ How does Lambda differ from Anonymous Classes?
3.​ Why are variables in Lambdas "effectively final"?
4.​ How does Lambda impact performance?
5.​ Can a Lambda implement multiple methods?
6.​ What are some real-world use cases of Lambda in Java?
7.​ How does Lambda work with Streams?
8.​ Can Lambda expressions throw exceptions?

10. Advanced Example: Sorting with Lambda


java

import [Link].*;

public class LambdaSorting {


public static void main(String[] args) {
List<String> names = [Link]("Avinash", "Chhaya",
"Rahul", "Sneha");

[Link]((s1, s2) -> [Link](s2)); // Sorting in


ascending order

[Link](names); // [Avinash, Chhaya, Rahul,


Sneha]
}
}

🔹 Lambdas make sorting simpler!


1. What is a Functional Interface?
●​ An interface with only one abstract method.
●​ It may have default methods and static methods.
●​ Can be annotated with @FunctionalInterface (optional but recommended).
●​ Enables Lambda expressions and Method References.

2. Examples of Functional Interfaces


1. Defining a Custom Functional Interface
java

@FunctionalInterface
interface MyFunctionalInterface {
void show(); // Only one abstract method
}

2. Using a Functional Interface with Lambda


java

public class LambdaExample {


public static void main(String[] args) {
MyFunctionalInterface obj = () -> [Link]("Lambda
with Functional Interface!");
[Link](); // Output: Lambda with Functional Interface!
}
}

🔹 Lambda replaces the need for an implementation class!

3. Built-in Functional Interfaces in Java 8


Java 8 provides several built-in functional interfaces under the [Link]
package.
(1) Predicate<T>

●​ Tests a condition and returns true or false.


●​ Has test(T t) method.

Example: Check if a number is even


java

import [Link];

public class PredicateExample {


public static void main(String[] args) {
Predicate<Integer> isEven = x -> x % 2 == 0;
[Link]([Link](10)); // true
[Link]([Link](15)); // false
}
}

✅ Use Case: Filtering data in Streams.

(2) Consumer<T>

●​ Accepts a value but returns nothing.


●​ Has accept(T t) method.

Example: Print a name


java

import [Link];

public class ConsumerExample {


public static void main(String[] args) {
Consumer<String> printName = name ->
[Link]("Hello, " + name);
[Link]("Avinash"); // Output: Hello, Avinash
}
}

✅ Use Case: Logging, event handling, modifying elements.


(3) Function<T, R>

●​ Takes an input and returns an output.


●​ Has apply(T t) method.

Example: Square a number


java

import [Link];

public class FunctionExample {


public static void main(String[] args) {
Function<Integer, Integer> square = x -> x * x;
[Link]([Link](4)); // 16
}
}

✅ Use Case: Transforming data.

(4) Supplier<T>

●​ Takes no input and returns a value.


●​ Has get() method.

Example: Generate a random number


java

import [Link];

public class SupplierExample {


public static void main(String[] args) {
Supplier<Double> randomNumber = () -> [Link]();
[Link]([Link]()); // Random number
}
}

✅ Use Case: Lazy initialization, fetching configurations.


4. Combining Functional Interfaces
We can combine multiple functional interfaces to create more powerful functions.

Example: Predicate Chaining


java

import [Link];

public class PredicateChaining {


public static void main(String[] args) {
Predicate<Integer> isEven = x -> x % 2 == 0;
Predicate<Integer> isGreaterThan10 = x -> x > 10;

Predicate<Integer> combined = [Link](isGreaterThan10);

[Link]([Link](12)); // true
[Link]([Link](8)); // false
}
}

🔹 .and(), .or(), .negate() help combine conditions!


Example: Function Chaining

java

import [Link];

public class FunctionChaining {


public static void main(String[] args) {
Function<Integer, Integer> multiplyBy2 = x -> x * 2;
Function<Integer, Integer> add10 = x -> x + 10;

Function<Integer, Integer> combined =


[Link](add10);

[Link]([Link](5)); // (5 * 2) + 10 = 20
}
}

🔹 .andThen() applies functions sequentially!


5. @FunctionalInterface Annotation
This annotation ensures that an interface remains functional by preventing additional
abstract methods.

Example: Valid Functional Interface


java

@FunctionalInterface
interface ValidFunctionalInterface {
void show();

default void print() { // Allowed


[Link]("Default Method");
}
}

Example: Invalid Functional Interface (Compilation Error)


java

@FunctionalInterface
interface InvalidFunctionalInterface {
void show();
void display(); // Error: More than one abstract method
}

🔹 @FunctionalInterface prevents accidental method additions.

6. Custom Functional Interface with Generics


We can define generic functional interfaces.

java

@FunctionalInterface
interface GenericFunction<T> {
T operate(T a, T b);
}

public class GenericLambda {


public static void main(String[] args) {
GenericFunction<Integer> sum = (a, b) -> a + b;
[Link]([Link](10, 20)); // Output: 30
}
}

🔹 Generic functional interfaces improve reusability!

7. Functional Interfaces with Method References


Lambdas can be replaced by Method References.

Example: Using Method Reference


java

import [Link];

public class MethodReferenceExample {


public static void printMessage(String message) {
[Link](message);
}

public static void main(String[] args) {


Consumer<String> messagePrinter =
MethodReferenceExample::printMessage;
[Link]("Hello, Java 8!"); // Output: Hello,
Java 8!
}
}

🔹 MethodReferenceExample::printMessage is a shortcut for message ->


printMessage(message)!
8. Real-World Use Cases
1.​ Lambda + Streams (Filtering Data)

java

import [Link];
import [Link];
import [Link];

public class StreamFiltering {


public static void main(String[] args) {
List<String> names = [Link]("Avinash", "Rahul",
"Chhaya", "Sneha");
Predicate<String> startsWithA = name ->
[Link]("A");

[Link]().filter(startsWithA).forEach([Link]::println);
// Output: Avinash
}
}

2.​ Using Consumers for Logging

java

import [Link];

public class Logger {


public static void main(String[] args) {
Consumer<String> log = message -> [Link]("[LOG]:
" + message);
[Link]("Application Started");
}
}
9. Common Interview Questions
1.​ What is a Functional Interface?
2.​ Why do we use @FunctionalInterface annotation?
3.​ Difference between Predicate, Function, Consumer, and Supplier?
4.​ How to chain functional interfaces together?
5.​ How are Functional Interfaces used in Streams API?
6.​ What are Method References?
7.​ Why do variables inside Lambda need to be effectively final?
8.​ What is the role of default and static methods in functional interfaces?
1. What is a Stream?
A Stream is a sequence of elements that:

●​ Does NOT store data (it processes data from a source).


●​ Performs computations in a functional style.
●​ Supports method chaining (fluent API).
●​ Can be parallelized for performance.

✅ Streams work with data sources like Collections, Arrays, Files, etc.

2. Creating Streams
We can create Streams from different sources.

(1) Creating a Stream from a List


java

import [Link];
import [Link];
import [Link];

public class StreamExample {


public static void main(String[] args) {
List<String> names = [Link]("Avinash", "Rahul",
"Chhaya", "Sneha");

// Convert List to Stream


Stream<String> nameStream = [Link]();

// Print each element


[Link]([Link]::println);
}
}

(2) Creating a Stream from an Array


java

import [Link];
public class StreamFromArray {
public static void main(String[] args) {
String[] languages = {"Java", "Python", "C++"};

Stream<String> langStream = [Link](languages);


[Link]([Link]::println);
}
}

(3) Creating a Stream using [Link]()


java

import [Link];

public class StreamGenerateExample {


public static void main(String[] args) {
Stream<Integer> infiniteStream = [Link](() -> 1);

[Link](5).forEach([Link]::println); //
Prints 1 five times
}
}

3. Intermediate vs Terminal Operations


Stream operations are classified into:

(A) Intermediate Operations (Return Stream)

✅ Lazy evaluation – executed only when a terminal operation is called.


●​ filter(Predicate<T>) – Filters elements based on a condition.
●​ map(Function<T, R>) – Transforms elements.
●​ sorted() – Sorts elements.
●​ distinct() – Removes duplicates.
●​ limit(n) – Limits the stream to n elements.
●​ peek(Consumer<T>) – Performs an action (for debugging).
(B) Terminal Operations (Consume Stream)

✅ Final operation – no further operations after this.


●​ forEach(Consumer<T>) – Iterates over elements.
●​ collect(Collector<T, A, R>) – Converts stream to a collection.
●​ count() – Counts elements.
●​ min() / max() – Finds min/max element.
●​ reduce(BinaryOperator<T>) – Reduces elements to a single value.
●​ toArray() – Converts stream to an array.

4. Stream Operations in Action

(1) Filtering Elements using filter()


java

import [Link];
import [Link];
import [Link];

public class StreamFilterExample {


public static void main(String[] args) {
List<String> names = [Link]("Avinash", "Rahul",
"Chhaya", "Sneha");

List<String> filteredNames = [Link]()


.filter(name ->
[Link]("A"))

.collect([Link]());

[Link](filteredNames); // Output: [Avinash]


}
}
(2) Transforming Data using map()
java

import [Link];
import [Link];
import [Link];

public class StreamMapExample {


public static void main(String[] args) {
List<String> names = [Link]("Avinash", "Rahul",
"Chhaya");

List<Integer> nameLengths = [Link]()


.map(String::length)

.collect([Link]());

[Link](nameLengths); // Output: [7, 5, 6]


}
}

(3) Sorting Elements using sorted()


java

import [Link];
import [Link];

public class StreamSortedExample {


public static void main(String[] args) {
List<Integer> numbers = [Link](5, 2, 8, 1, 3);

[Link]()
.sorted()
.forEach([Link]::println);
// Output: 1 2 3 5 8
}
}
(4) Reducing Values using reduce()
java

import [Link];
import [Link];

public class StreamReduceExample {


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

int sum = [Link]()


.reduce(0, Integer::sum);

[Link](sum); // Output: 15
}
}

(5) Collecting Results using collect()


java

import [Link];
import [Link];
import [Link];

public class StreamCollectExample {


public static void main(String[] args) {
List<String> names = [Link]("Avinash", "Rahul",
"Chhaya");

List<String> upperCaseNames = [Link]()


.map(String::toUpperCase)

.collect([Link]());

[Link](upperCaseNames); // Output: [AVINASH,


RAHUL, CHHAYA]
}
}
5. Parallel Streams (Performance Boost)
Parallel streams divide tasks across multiple threads.

Example: Parallel Processing


java

import [Link];
import [Link];

public class ParallelStreamExample {


public static void main(String[] args) {
List<String> names = [Link]("Avinash", "Rahul",
"Chhaya");

[Link]().forEach([Link]::println);
}
}

✅ Use parallel streams for large data processing but avoid for small tasks.
6. Common Interview Questions
1.​ What is the difference between map() and flatMap()?
○​ map() transforms each element individually.
○​ flatMap() flattens nested structures (List of Lists → Single List).
2.​ What is the difference between findFirst() and findAny()?
○​ findFirst() returns the first element.
○​ findAny() returns any element (useful in parallel streams).
3.​ Why is Stream lazy?
○​ Intermediate operations do not execute immediately; only when a terminal
operation is called.
4.​ How does parallelStream() work?
○​ It splits data into chunks and processes them in parallel using
ForkJoinPool.
5.​ What happens if you reuse a Stream after a terminal operation?
○​ A Stream cannot be reused; it will throw IllegalStateException.
6.​ Difference between Collection API and Streams API?
○​ Collection API stores & manipulates data.
○​ Streams API processes data functionally without modifying the source.
✅ Optional in Java 8
The Optional class in Java 8 helps avoid NullPointerException (NPE) by representing
optional (nullable) values safely.

1. What is Optional?
✅ Optional<T> is a container object that may or may not contain a value.​
✅ It prevents null checks and reduces NullPointerException risks.​
✅ It forces developers to handle missing values explicitly.

2. Creating an Optional Object


There are multiple ways to create an Optional instance:

(1) Using [Link]() (Non-null values)


java

import [Link];

public class OptionalExample {


public static void main(String[] args) {
Optional<String> name = [Link]("Avinash");
[Link]([Link]()); // Output: Avinash
}
}

⚠️ Throws NullPointerException if value is null.

(2) Using [Link]() (Handles null safely)


java

import [Link];

public class OptionalOfNullableExample {


public static void main(String[] args) {
Optional<String> name = [Link](null);
[Link]([Link]()); // Output: false
}
}

✅ Returns [Link]() for null.

(3) Using [Link]()


java

import [Link];

public class OptionalEmptyExample {


public static void main(String[] args) {
Optional<String> emptyOpt = [Link]();
[Link]([Link]()); // Output: false
}
}

✅ Represents an absent value explicitly.

3. Checking Presence of Value

(1) Using isPresent()


java

Optional<String> opt = [Link](null);


[Link]([Link]()); // Output: false

(2) Using ifPresent() (Execute if value exists)


java

Optional<String> opt = [Link]("Hello");


[Link]([Link]::println); // Output: Hello
4. Retrieving Values from Optional

(1) Using get() ( ⚠️ Avoid when unsure)


java

Optional<String> opt = [Link](null);


[Link]([Link]()); // Throws NoSuchElementException

⚠️ Never use get() unless you're sure the value is present.

(2) Using orElse() (Default Value)


java

Optional<String> opt = [Link](null);


[Link]([Link]("Default Value")); // Output: Default
Value

(3) Using orElseGet() (Supplier Function)


java

Optional<String> opt = [Link](null);


[Link]([Link](() -> "Generated Value")); //
Output: Generated Value

✅ orElse() always executes the default code.​


✅ orElseGet() executes only if value is null.

(4) Using orElseThrow() (Throw Exception for Missing Value)


java

import [Link];

public class OptionalOrElseThrowExample {


public static void main(String[] args) {
Optional<String> opt = [Link](null);
String value = [Link](() -> new
RuntimeException("Value is absent!"));
[Link](value);
}
}

⚠️ Throws custom exception if the value is missing.

5. Transforming Optional Values

(1) Using map()

●​ Transforms the value inside Optional if present.

java

Optional<String> opt = [Link]("Avinash");


Optional<Integer> lengthOpt = [Link](String::length);
[Link]([Link]()); // Output: 7

(2) Using flatMap()

●​ Used when map() returns an Optional itself (prevents nested


Optional<Optional<T>>).

java

Optional<String> opt = [Link]("Avinash");


Optional<Optional<Integer>> nestedOpt = [Link](name ->
[Link]([Link]()));

Optional<Integer> flatMappedOpt = [Link](name ->


[Link]([Link]()));
[Link]([Link]()); // Output: 7
6. Filtering Values
●​ filter() keeps the value only if it matches the condition.

java

Optional<String> opt = [Link]("Java");


Optional<String> filteredOpt = [Link](s -> [Link]() > 3);
[Link]([Link]()); // Output: true

✅ Returns [Link]() if the condition fails.


7. Using Optional in Method Return Values (Best
Practice)
Before Java 8 (Risky Code)
java

public String getUserName() {


return null; // Risk of NullPointerException
}

With Java 8 Optional (Safer)


java

import [Link];

public class UserService {


public Optional<String> getUserName() {
return [Link](null); // Safe handling of null
}

public static void main(String[] args) {


UserService service = new UserService();
[Link]([Link]().orElse("No User
Found")); // Output: No User Found
}
}
8. Interview Questions on Optional
1.​ What is Optional in Java? Why is it introduced?
○​ Optional is a container for nullable values to avoid
NullPointerException.
2.​ What is the difference between orElse() and orElseGet()?
○​ orElse() always executes the default value code.
○​ orElseGet() executes only if the value is missing.
3.​ When should we use flatMap() instead of map()?
○​ Use flatMap() when the function returns an Optional<T> to avoid
Optional<Optional<T>>.
4.​ How does filter() work with Optional?
○​ It keeps the value only if it satisfies the given condition.
5.​ Why should we avoid [Link]()?
○​ It throws NoSuchElementException if no value is present.
✅ Java 8: Default and Static Methods in Interfaces
1. Why Were Default and Static Methods Introduced?

Before Java 8, interfaces could only have abstract methods, and all implementing classes
had to define those methods. This caused problems:

●​ If a new method was added to an interface, all implementing classes had to be


modified.
●​ It broke backward compatibility.


To solve this, Java 8 introduced:​


Default methods (methods with a body inside an interface).​
Static methods (utility/helper methods inside interfaces).

2. Default Methods in Interfaces


Definition

A default method is a method in an interface that has a default implementation using the
default keyword.

Example: Default Method in an Interface


java
interface Vehicle {
default void start() {
[Link]("Starting vehicle...");
}
}
class Car implements Vehicle {
// No need to override start() unless we want a custom behavior
}
public class DefaultMethodExample {
public static void main(String[] args) {
Car car = new Car();
[Link](); // Output: Starting vehicle...
}
}

✅ The Car class inherits the start() method from Vehicle without needing to
implement it.
3. Overriding Default Methods
If needed, a class can override the default method.

java

interface Vehicle {
default void start() {
[Link]("Starting vehicle...");
}
}

class Car implements Vehicle {


@Override
public void start() {
[Link]("Car is starting...");
}
}

public class OverridingDefaultMethod {


public static void main(String[] args) {
Car car = new Car();
[Link](); // Output: Car is starting...
}
}

✅ The Car class overrides the default method with its own implementation.
4. Resolving Multiple Default Methods in Interfaces
(Diamond Problem)
What happens if multiple interfaces provide the same default method?

java

interface Vehicle {
default void start() {
[Link]("Vehicle starting...");
}
}

interface Machine {
default void start() {
[Link]("Machine starting...");
}
}

class Car implements Vehicle, Machine {


// Compilation Error: start() is inherited from both Vehicle and
Machine
}

⚠️ Compilation error! Java does not know which start() method to use.
Solution: Explicitly Override the Method

We must override start() in Car and specify which method to call.

java

class Car implements Vehicle, Machine {


@Override
public void start() {
[Link](); // Calling Vehicle's start()
}
}

✅ [Link]() resolves ambiguity by explicitly choosing one interface method.


5. Static Methods in Interfaces
Unlike default methods, static methods cannot be overridden. They belong to the
interface itself.

Example: Static Method in an Interface


java

interface Utility {
static void showHelp() {
[Link]("This is a utility method.");
}
}

public class StaticMethodExample {


public static void main(String[] args) {
[Link](); // Output: This is a utility method.
}
}

✅ Static methods are called using the interface name ([Link]()), not an
instance.

6. Key Differences: Default vs. Static Methods


Feature Default Method Static Method

Defined in Interface? ✅ Yes ✅ Yes


Provides Implementation? ✅ Yes ✅ Yes
Can Be Overridden? ✅ Yes ❌ No
Access via Object? ✅ Yes ❌ No (Use Interface Name)
Resolves Interface ✅ Yes ❌ No
Updates?
7. Interview Questions on Default & Static Methods
1.​ Why were default methods introduced in Java 8?
○​ To allow interfaces to evolve without breaking existing implementations.
2.​ Can an interface have both abstract and default methods?
○​ Yes, an interface can have both.
3.​ What happens if two interfaces have the same default method?
○​ The implementing class must override and resolve the conflict.
4.​ Can we override static methods in interfaces?
○​ No, static methods belong to the interface, not instances.
5.​ How do we call a default method of a specific interface inside a class?
○​ Using [Link]().

Common questions

Powered by AI

The introduction of default and static methods in Java 8 interfaces allows for backward compatibility by enabling interfaces to be extended with new methods without breaking existing implementations. Default methods provide a standard implementation using the default keyword, allowing implementing classes to override it if necessary. Static methods define utility functions that can be called on the interface itself. However, potential drawbacks include complexity and ambiguity when multiple interfaces provide the same default method, leading to the diamond problem where an implementing class must resolve which method to use. Additionally, static methods in interfaces blur the lines between interfaces and classes due to their encapsulation of behavior, challenging the single-responsibility principle .

Developers might avoid using lambda expressions in scenarios where the logic is too complex and would compromise readability or when performance considerations favor anonymous classes, which may be more efficient in certain contexts. If lambda usage detracts from code clarity, traditional methods or classes could be employed instead. In situations demanding detailed debugging, explicit classes are preferable due to their named nature and accessible structure. While lambdas excel in conciseness, they are less suited for scenarios requiring detailed control or explanation of behavior .

Method references in Java 8 provide a shorthand notation for calling a method via a compact lambda expression, enhancing readability and allowing reuse of existing method implementations without explicitly defining a lambda. The syntax for method references is ClassName::methodName, replacing the need for a lambda that only calls a method. For example, forEach(System.out::println) in a Stream API context will print each element, using System.out.println as a method reference instead of a lambda like x -> System.out.println(x). Method references improve clarity by abstracting boilerplate code, directly expressing the functional intent .

The Optional class in Java 8 is designed to represent optional (nullable) values explicitly, addressing common pitfalls like NullPointerException by encouraging safe handling of potential null references. Optional acts as a container that may hold a value or be empty, providing methods such as isPresent, orElse, and orElseThrow to handle absent values explicitly. This design pattern minimizes the need for null checks and clarifies the developer's intent to handle the absence of a value, thus making the code more robust and self-explanatory .

The get() method of the Optional class should be avoided because it throws a NoSuchElementException if no value is present, which negates the safety that Optional provides against null references. Instead, safer alternatives include using orElse for a default value when Optional is empty, orElseGet for lazily evaluated defaults, orElseThrow for custom exception handling, and isPresent or ifPresent to check or interact with present values conditionally. These methods facilitate null-safe operations without unexpected exceptions, reinforcing reliable code design by explicitly handling absent values .

Functional interfaces, which have exactly one abstract method, provide the target type for lambda expressions in Java 8. They facilitate passing behavior as parameters and support the concise implementation of single-method interfaces using lambdas. Common use cases include Predicate for boolean conditions, Consumer for operations that accept a single input with no return, Function for applying transformations, and Supplier for generating or supplying values. The @FunctionalInterface annotation is used to ensure that an interface remains functional, preventing accidental additions of abstract methods. These interfaces enable more functional programming paradigms by integrating lambdas smoothly into Java’s type system .

Lambda expressions in Java 8 are a way to implement functional programming by creating anonymous functions, providing a more concise and readable alternative to anonymous inner classes. They allow for more succinct code by omitting the boilerplate syntax associated with anonymous classes. For example, a Comparator implementation using an anonymous class requires overriding the compare method, while a lambda can implement the same functionality with simpler syntax: (s1, s2) -> s1.compareTo(s2). Thus, lambda expressions improve readability while maintaining the ability to create function-like structures without a name .

Lambda expressions in Java 8 capture variables from their surrounding scope, akin to anonymous inner classes. Variables accessed within a lambda expression must be effectively final, meaning they cannot be modified after they are initialized outside of the lambda. This constraint ensures thread safety and predictability in concurrent environments. It emphasizes immutability, promoting functional programming principles. This behavior helps prevent unexpected side effects and enables safe, concurrent code execution but also restricts the lambda's flexibility by forbidding the update of captured external variables .

The Streams API in Java 8 provides a new approach to processing sequences of elements in a functional style, differing significantly from traditional iterations. Streams allow for declarative processing, enabling operations like filter, map, reduce, and collect to be expressed as a chain of methods. This supports more readable and concise code compared to explicit loops. Streams do not store data; instead, they process data from a source and can be parallelized for performance. Unlike traditional loops that modify the underlying collection, streams provide a functional, immutable view of data, enhancing both code clarity and parallel execution capabilities .

Java 8's functional interfaces have significantly shifted Java's programming paradigm towards functional programming, allowing developers to pass behavior as parameters and support more declarative approaches to problem-solving. By enabling concise lambda expressions, they reduce boilerplate code and introduce immutability and stateless behavior, which are central to functional programming. This evolution integrates seamlessly with the Streams API, enabling new patterns such as map-reduce processing over data collections. As a result, Java has transitioned from strictly object-oriented programming to a hybrid model, offering greater flexibility and expressive power while retaining backward compatibility .

You might also like