Modern Java
Optional and Modern Null Handling
আপনি একটি free preview lesson দেখছেন।
Lesson Overview
Java-তে null খুব পুরোনো এবং গুরুত্বপূর্ণ একটি concept।
কোনো reference:
Course course =
null;
হতে পারে।
কিন্তু null-এর সবচেয়ে বড় সমস্যা হলো:
absence অনেক সময় implicit থাকে
Method signature দেখে বোঝা যায় না result:
always থাকবে
নাকি
missing হতে পারে
Example:
Course findCourse(
String code
)
এই method কি:
Course return করবে?
null return করতে পারে?
exception throw করবে?
Signature দেখে clear নয়।
Modern Java-তে এই ধরনের optional result explicitভাবে represent করার জন্য আমরা ব্যবহার করতে পারি:
Optional<T>
Example:
Optional<Course> findCourse(
String code
)
এখন method signature নিজেই বলে:
Course থাকতে পারে
অথবা
না-ও থাকতে পারে।
এই lesson-এ আমরা শিখব:
nullকোথায় problem তৈরি করেNullPointerExceptionOptional<T>কীOptional.empty()Optional.of()Optional.ofNullable()isPresent()isEmpty()ifPresent()ifPresentOrElse()map()flatMap()filter()orElse()orElseGet()orElseThrow()or()- Optional chaining
- Optional return type কখন useful
Optional.get()কেন avoid করা ভালো- Optional parameter হিসেবে blindly ব্যবহার করা কেন উচিত নয়
- Optional field হিসেবে overuse কেন problematic হতে পারে
- Null validation এবং domain invariants
- Practical null-handling strategies
The Problem with null
Suppose:
Course course =
findCourse(
"JAVA"
);
Then:
System.out.println(
course.title()
);
যদি findCourse() return করে:
null
তাহলে code throw করবে:
NullPointerException
NullPointerException
NullPointerException হয় যখন আমরা null reference-এর মাধ্যমে instance member access করার চেষ্টা করি।
Example:
String value =
null;
System.out.println(
value.length()
);
এখানে:
value
কোনো actual String object-কে point করছে না।
তাই:
value.length()
call করা সম্ভব নয়।
Traditional Null Check
একটি common approach:
Course course =
findCourse(
"JAVA"
);
if (
course != null
) {
System.out.println(
course.title()
);
}
এটি perfectly valid।
Optional মানে এই নয় যে Java code-এ আর কখনো null check থাকবে না।
কিন্তু কিছু API boundary-তে Optional absence-কে clearerভাবে express করতে পারে।
What Is Optional<T>?
Optional<T> এমন একটি container-like type যা represent করে:
একটি non-null value আছে
অথবা:
কোনো value নেই
Example:
Optional<Course>
এর দুইটি possible state:
Optional containing Course
Optional.empty()
Important Mental Model
Optional<Course> মানে:
Course অথবা null
literally নয়।
Better mental model:
একটি result আছে কি না
সেটি explicitly represent করা হয়েছে।
Creating an Empty Optional
Optional<Course> course =
Optional.empty();
Meaning:
Course পাওয়া যায়নি।
Optional.of()
Known non-null value থাকলে:
Course course =
new Course(
"JAVA",
"Java Foundation"
);
Optional<Course> result =
Optional.of(
course
);
Important Rule for Optional.of()
এইটি safe only when value definitely non-null।
Optional.of(
value
);
যদি:
value == null
হয়, তাহলে exception হবে।
So:
Optional.of(...)
means:
আমি জানি value non-null।
Optional.ofNullable()
Value null হতে পারে এমন situation-এ:
Optional<Course> result =
Optional.ofNullable(
course
);
If:
course != null
then Optional contains Course।
If:
course == null
then:
Optional.empty()
of() vs ofNullable()
Use:
Optional.of(
value
)
when:
null is not valid here
Use:
Optional.ofNullable(
value
)
when:
source value may legitimately be null
Example Repository Lookup
Suppose repository internally uses a Map:
Map<String, Course> courses =
new HashMap<>();
Map lookup:
courses.get(
code
)
returns:
Course
or:
null
We can expose a clearer API:
Optional<Course> findByCode(
String code
) {
return Optional.ofNullable(
courses.get(
code
)
);
}
Caller এখন signature দেখে জানে:
result missing হতে পারে।
isPresent()
Check:
Optional<Course> result =
findByCode(
"JAVA"
);
if (
result.isPresent()
) {
...
}
isPresent() returns:
true
when value exists।
Accessing the Value
Technically:
if (
result.isPresent()
) {
Course course =
result.get();
}
works।
কিন্তু এই style অনেক সময় Optional-এর benefit কমিয়ে দেয়।
আমরা একটু পরে দেখব better APIs।
isEmpty()
Check absence:
if (
result.isEmpty()
) {
System.out.println(
"Course not found."
);
}
এটি অনেক সময়:
!result.isPresent()
এর চেয়ে readable।
Optional.get()
get() contained value return করে।
Course course =
result.get();
কিন্তু Optional empty হলে:
NoSuchElementException
throw করবে।
Why get() Is Usually a Bad Default
এই code:
Course course =
findByCode(
code
).get();
আসলে missing case ignore করছে।
Optional-এর purpose ছিল:
absence explicit করা
কিন্তু get() দিয়ে blindly value বের করলে আমরা আবার unsafe assumption করছি।
Better APIs:
orElse(...)
orElseGet(...)
orElseThrow(...)
ifPresent(...)
map(...)
ifPresent()
যদি value থাকে তখন একটি action run করতে চাই:
findByCode(
"JAVA"
).ifPresent(
course ->
System.out.println(
course.title()
)
);
If Course missing:
nothing happens
Method Reference
Optional<String> title =
Optional.of(
"Java"
);
title.ifPresent(
System.out::println
);
When ifPresent() Is Useful
Use when requirement is:
value থাকলে এই কাজ করো
absence হলে কিছু করার দরকার নেই
Example:
optional audit value print করা
optional cache entry process করা
optional lookup result display করা
ifPresentOrElse()
যদি presence এবং absence দুইটির জন্য আলাদা behavior দরকার হয়:
result.ifPresentOrElse(
course ->
System.out.println(
course.title()
),
() ->
System.out.println(
"Course not found."
)
);
First argument:
value থাকলে কী হবে
Second:
value না থাকলে কী হবে
Optional.map()
Optional.map() contained value থাকলে transform করে।
Suppose:
Optional<Course> course =
findByCode(
"JAVA"
);
Course থেকে title চাই।
Optional<String> title =
course.map(
Course::title
);
What Happens If Optional Is Empty?
If:
course
is:
Optional.empty()
then:
course.map(
Course::title
)
returns:
Optional.empty()
Mapping function run করে না।
Optional map() Mental Model
Value আছে?
↓ yes
transform করো
Value নেই?
↓
empty-ই থাকো
Avoid Manual Presence Checks
Instead of:
Optional<Course> result =
findByCode(
code
);
if (
result.isPresent()
) {
Course course =
result.get();
String title =
course.title();
...
}
we can write:
Optional<String> title =
findByCode(
code
).map(
Course::title
);
আরও directly transformation express করছে।
Chaining map()
Suppose:
record Course(
String code,
Instructor instructor
) {
}
record Instructor(
String name
) {
}
If Course exists:
Optional<String> instructorName =
findByCode(
"JAVA"
)
.map(
Course::instructor
)
.map(
Instructor::name
);
Chaining Means Absence Propagates
If Course missing:
empty
then next mappings run করবে না।
If Course exists, instructor transform হবে।
This reduces repeated:
if (
value != null
)
chains।
Null Returned from map()
If mapping function returns null, Optional.map() result becomes empty।
Still, ideally mapping functions should have clear null contracts।
Better domain design often avoids nullable intermediate values where absence has explicit meaning।
Optional.filter()
Optional-এর contained value condition satisfy করলে রাখে।
Example:
Optional<Course> publishedCourse =
findByCode(
"JAVA"
).filter(
Course::published
);
Behavior
If Course:
exists
AND
published
then result contains Course।
Otherwise:
Optional.empty()
Another Example
Optional<Course> premium =
findByCode(
code
).filter(
course ->
course.priceInPaisa()
>= 500_000
);
filter() Mental Model
Optional value আছে?
↓
condition true?
↓
yes → keep
no → empty
orElse()
Value না থাকলে default value দিতে:
String title =
findByCode(
code
)
.map(
Course::title
)
.orElse(
"Unknown Course"
);
If found:
actual title
If missing:
Unknown Course
Simple orElse() Example
Optional<String> value =
Optional.empty();
String result =
value.orElse(
"Default"
);
Result:
Default
orElseGet()
Fallback value যদি lazily create করতে চাই:
String title =
optionalTitle.orElseGet(
() ->
buildFallbackTitle()
);
Fallback supplier only needed হলে execute হবে।
orElse() vs orElseGet()
এটি important।
With:
optional.orElse(
createFallback()
);
createFallback() argument evaluate হবে method call-এর আগে।
অর্থাৎ Optional value present হলেও expensive fallback তৈরি হতে পারে।
orElseGet() Is Lazy
optional.orElseGet(
() ->
createFallback()
);
এখানে createFallback() only run করবে যখন Optional empty।
Example
Suppose:
String fallback =
loadFromRemoteService();
expensive।
Avoid:
optional.orElse(
loadFromRemoteService()
);
when unnecessary execution matters।
Prefer:
optional.orElseGet(
() ->
loadFromRemoteService()
);
When orElse() Is Fine
Simple constant fallback:
.orElse(
"Unknown"
)
No issue।
For cheap existing object:
.orElse(
defaultCourse
)
also fine।
orElseThrow()
যদি absence exceptional হয়:
Course course =
findByCode(
code
).orElseThrow(
() ->
new IllegalArgumentException(
"Course not found: "
+ code
)
);
এটি খুব common pattern।
Mental Model
Course আছে?
→ return Course
Course নেই?
→ throw exception
Why orElseThrow() Is Better Than get()
Compare:
findByCode(
code
).get();
Failure:
NoSuchElementException
with little domain meaning।
Better:
findByCode(
code
).orElseThrow(
() ->
new CourseNotFoundException(
code
)
);
Now failure expresses:
actual domain problem
Custom Exception Example
final class CourseNotFoundException
extends RuntimeException {
CourseNotFoundException(
String code
) {
super(
"Course not found: "
+ code
);
}
}
Then:
Course course =
repository.findByCode(
code
).orElseThrow(
() ->
new CourseNotFoundException(
code
)
);
No-Argument orElseThrow()
Optional also supports:
optional.orElseThrow();
If empty, it throws:
NoSuchElementException
Usually domain code-এ meaningful exception supplier clearer হতে পারে।
or()
Suppose first lookup fails হলে second Optional-producing source try করতে চাই।
Example:
Optional<Course> course =
primaryRepository.findByCode(
code
).or(
() ->
backupRepository.findByCode(
code
)
);
Why or() Is Different from orElseGet()
orElseGet() gives:
T
fallback।
or() gives:
Optional<T>
fallback।
So Optional-producing fallback chain-এর জন্য or() useful।
Optional Chaining Example
Suppose:
Optional<Course> course =
findByCode(
code
);
Need published Course title or fallback:
String title =
course
.filter(
Course::published
)
.map(
Course::title
)
.orElse(
"Unavailable"
);
Read:
Course থাকলে
↓
published হলে রাখো
↓
title বের করো
↓
না থাকলে "Unavailable"
This Is Where Optional Becomes Useful
Without Optional:
Course course =
findByCodeNullable(
code
);
String title;
if (
course == null
|| !course.published()
) {
title =
"Unavailable";
} else {
title =
course.title();
}
Optional version:
String title =
findByCode(
code
)
.filter(
Course::published
)
.map(
Course::title
)
.orElse(
"Unavailable"
);
দুইটিই valid।
কিন্তু second version absence/transformation flow compactভাবে express করছে।
flatMap() with Optional
Stream-এর মতো Optional-এও flatMap() আছে।
এটি useful যখন mapping function নিজেই:
Optional<R>
return করে।
Example
Suppose:
Optional<Course> findCourse(
String code
)
and:
Optional<Instructor> findInstructor(
Course course
)
If we do:
course.map(
this::findInstructor
)
result becomes:
Optional<Optional<Instructor>>
Nested Optional।
Use flatMap()
Optional<Instructor> instructor =
course.flatMap(
this::findInstructor
);
Now nested Optional flatten হয়ে:
Optional<Instructor>
হয়।
map() vs flatMap() for Optional
Same core idea as Stream।
If mapper returns normal value:
Course
→ String
use:
map()
If mapper returns Optional:
Course
→ Optional<Instructor>
use:
flatMap()
Example Chain
Optional<String> instructorName =
findCourse(
code
)
.flatMap(
this::findInstructor
)
.map(
Instructor::name
);
Flow:
Course exists?
↓
Instructor exists?
↓
name
যেকোনো stage missing হলে result:
Optional.empty()
Avoid Optional<Optional<T>>
যদি code-এ দেখেন:
Optional<Optional<Course>>
অনেক ক্ষেত্রে সেটি signal হতে পারে:
flatMap() দরকার
অথবা API abstraction rethink করা দরকার।
Optional Return Types
Optional<T> সবচেয়ে natural যখন method:
একটি result search করছে
এবং absence normal possibility
Example:
Optional<Course> findByCode(
String code
);
Good Optional Return Examples
Optional<Learner> findByEmail(
String email
);
Optional<Course> findByCode(
String code
);
Optional<Enrollment> findActiveEnrollment(
long learnerId
);
যদি "not found" normal result হয়।
When Optional May Not Be Needed
Suppose method contract guarantees Course must exist:
Course getRequiredCourse(
String code
)
and absence means programming/domain error।
Then method itself exception throw করতে পারে।
Example:
Course getRequiredCourse(
String code
) {
return repository.findByCode(
code
).orElseThrow(
() ->
new CourseNotFoundException(
code
)
);
}
Caller gets:
Course
because this application-service boundary guarantees either:
Course
or:
exception
find vs getRequired
Naming can communicate semantics।
Example:
Optional<Course> findByCode(
String code
)
suggests:
absence normal
While:
Course requireByCode(
String code
)
suggests:
must exist
Clear APIs reduce ambiguity।
Optional Is Not a Replacement for Validation
Suppose:
Course(
null,
"Java"
)
is invalid।
Do not solve invariant problem by making every field:
Optional<String>
Instead constructor validation:
this.code =
Objects.requireNonNull(
code
);
may be correct।
Required Data Should Usually Be Required
If Course must always have title:
record Course(
String code,
String title
) {
Course {
Objects.requireNonNull(
code
);
Objects.requireNonNull(
title
);
}
}
Do not make:
Optional<String> title
just because null exists in Java।
Optional Means Legitimate Absence
Good concept:
middleName may not exist
Potentially optional।
But:
Course code
if domain requires it:
not optional
The question is not:
Can Java represent null?
The question is:
Does absence make sense in the domain?
Optional as a Method Parameter
You can technically write:
void createCourse(
Optional<String> description
)
কিন্তু this is often awkward।
Caller must write:
createCourse(
Optional.of(
description
)
);
or:
createCourse(
Optional.empty()
);
Better Parameter Design
Depending on context, prefer:
createCourse(
String description
)
with explicit nullable contract if necessary,
or overloads,
or a request object with clear semantics।
Optional is most idiomatic as a:
return type
for potentially absent result।
Optional Fields
Technically possible:
class Course {
private Optional<String> description;
}
কিন্তু domain model-এর every optional field-কে Optional বানানো usually unnecessary।
It can introduce:
extra wrapping
serialization complexity
ORM/framework friction
awkward APIs
depending on environment।
Better Domain Modeling
If field is legitimately optional, you need an explicit project policy।
Possible strategies:
nullable internal field with strict boundary handling
dedicated value object
separate type/state
Optional-returning accessor
There is no universal rule that every nullable field should be stored as Optional।
Avoid Returning null Optional
Never write:
Optional<Course> findByCode(
String code
) {
return null;
}
This defeats the entire purpose।
An Optional-returning method should return:
Optional.of(...)
or:
Optional.empty()
but not:
null
Optional Itself Should Not Be Null
Caller should be able to assume:
Optional<Course> result =
findByCode(
code
);
result itself exists।
Its contents may be absent।
Do Not Wrap Collections in Optional Without Good Reason
Suppose:
List<Course> findPublishedCourses()
If there are no Courses, return:
List.of()
rather than:
Optional<List<Course>>
in most cases।
Why Empty Collection Is Better
A collection already naturally represents:
zero items
So:
List<Course>
can represent:
0
1
many
No need for another absence layer।
Prefer
List<Course> findAll()
returns:
[]
when no data।
Instead of:
Optional<List<Course>>
unless absence and empty collection truly mean different domain states।
Optional of Boolean Is Often Suspicious
Optional<Boolean>
can represent:
true
false
missing
Sometimes this genuinely matters।
But often an enum is clearer।
Example:
VerificationStatus {
VERIFIED,
NOT_VERIFIED,
UNKNOWN
}
can communicate semantics better than:
Optional<Boolean>
Optional Should Clarify, Not Hide
Good Optional usage:
Optional<Course> findByCode(...)
Clear:
maybe found
Poor design:
Optional<Optional<Boolean>>
Now semantics become hard to interpret।
Optional and Exceptions
Optional absence এবং exception একই concept নয়।
Optional:
No matching value
Exception:
Operation failed
Example:
Course not found
might be normal lookup absence।
But:
repository storage corrupted
is failure।
Do not convert every exception into:
Optional.empty()
Bad Example
try {
return Optional.of(
loadCourse()
);
} catch (
Exception exception
) {
return Optional.empty();
}
এটি dangerous।
Now caller cannot distinguish:
Course missing
from:
Disk failed
Parsing failed
Permission denied
Program bug
Preserve Failure Semantics
Optional is for:
absence
not:
swallow every error
Real failure should still be represented appropriately:
exception
error result
failure abstraction
depending on design।
Null at External Boundaries
Sometimes external systems return nullable values।
Examples:
legacy API
database driver
third-party library
old Java API
At boundary, we can normalize:
Optional.ofNullable(
externalValue
)
Then internal code can use explicit absence handling।
Example
String rawTitle =
legacyClient.findTitle(
code
);
Optional<String> title =
Optional.ofNullable(
rawTitle
);
But Do Not Wrap Everything Automatically
If external null means:
contract violation
then:
Objects.requireNonNull(...)
may be better।
Again:
What does absence mean?
comes first।
Objects.requireNonNull()
For required values:
this.code =
Objects.requireNonNull(
code,
"code must not be null"
);
This establishes invariant early।
Fail Fast
If Course code is required, fail at construction:
new Course(
null,
...
)
rather than allowing invalid object and failing much later।
This is called:
fail fast
Optional and Domain Invariants
Consider two different cases:
Case 1
Course title
must always exist।
Use:
required non-null field
Case 2
Search:
find Course by code
may find nothing।
Use:
Optional<Course>
These are different types of absence।
Optional Pipeline Example
Suppose:
Optional<Course> findByCode(
String code
)
and Course:
record Course(
String code,
String title,
long priceInPaisa,
boolean published
) {
}
Need:
published Course হলে title
otherwise "Unavailable"
String title =
findByCode(
code
)
.filter(
Course::published
)
.map(
Course::title
)
.orElse(
"Unavailable"
);
Another Pipeline Example
Need:
Course price
না থাকলে 0
long price =
findByCode(
code
)
.map(
Course::priceInPaisa
)
.orElse(
0L
);
But ask:
Does 0 actually mean missing?
If free Course can legitimately have:
price = 0
then this fallback loses information।
Maybe keeping:
Optional<Long>
is better।
Default Values Can Hide Meaning
This:
.orElse(
0L
)
may collapse:
Course missing
and:
Course exists and is free
into same result।
So default value selection must preserve semantics।
Optional Can Protect Meaning
Sometimes caller should explicitly deal with absence:
Optional<Long> price =
findByCode(
code
).map(
Course::priceInPaisa
);
rather than automatically defaulting।
orElseThrow() at Application Boundary
Repository:
Optional<Course> findByCode(
String code
);
Service:
Course findRequired(
String code
) {
return repository.findByCode(
code
).orElseThrow(
() ->
new CourseNotFoundException(
code
)
);
}
এটি clean layering pattern হতে পারে।
Repository says:
maybe exists
Application service decides:
এই use case-এ must exist
Complete Example
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
public class Main {
public static void main(String[] args) {
CourseRepository repository =
new CourseRepository();
repository.save(
new Course(
"JAVA",
"Java Foundation",
true
)
);
String title =
repository.findByCode(
"JAVA"
)
.filter(
Course::published
)
.map(
Course::title
)
.orElse(
"Unavailable"
);
System.out.println(
title
);
String missing =
repository.findByCode(
"BACKEND"
)
.map(
Course::title
)
.orElse(
"Not found"
);
System.out.println(
missing
);
}
record Course(
String code,
String title,
boolean published
) {
}
static final class CourseRepository {
private final Map<String, Course> courses =
new HashMap<>();
void save(
Course course
) {
courses.put(
course.code(),
course
);
}
Optional<Course> findByCode(
String code
) {
return Optional.ofNullable(
courses.get(
code
)
);
}
}
}
Output:
Java Foundation
Not found
Practical Example — Required Course
static Course requireCourse(
CourseRepository repository,
String code
) {
return repository.findByCode(
code
).orElseThrow(
() ->
new IllegalArgumentException(
"Course not found: "
+ code
)
);
}
Practical Example — Fallback Lookup
Suppose current code না পেলে legacy code lookup করতে চাই।
Optional<Course> course =
repository.findByCode(
currentCode
).or(
() ->
repository.findByCode(
legacyCode
)
);
Practical Example — Nested Optional
Suppose:
Optional<Course> findCourse(
String code
)
and:
Optional<Instructor> findInstructor(
Course course
)
Use:
Optional<String> instructorName =
findCourse(
code
)
.flatMap(
this::findInstructor
)
.map(
Instructor::name
);
Common Mistake 1 — Calling get() Immediately
Bad default:
repository.findByCode(
code
).get();
This ignores absence।
Prefer an API that expresses intended behavior:
orElse(...)
orElseGet(...)
orElseThrow(...)
map(...)
ifPresent(...)
Common Mistake 2 — isPresent() + get() Everywhere
Example:
if (
optional.isPresent()
) {
Course course =
optional.get();
}
Not always wrong।
কিন্তু যদি every Optional use এভাবেই লেখা হয়, তাহলে আপনি essentially nullable-style branching-ই লিখছেন।
Check whether:
map()
filter()
orElseThrow()
ifPresent()
intent better express করে কি না।
Common Mistake 3 — orElse() with Expensive Work
Avoid:
optional.orElse(
expensiveFallback()
);
if fallback expensive and usually unnecessary।
Use:
optional.orElseGet(
() ->
expensiveFallback()
);
Common Mistake 4 — Returning null from Optional Method
Never:
Optional<Course> findByCode(...) {
return null;
}
Return:
Optional.empty();
Common Mistake 5 — Optional Everywhere
Do not automatically write:
Optional<String> code
Optional<String> title
Optional<Long> price
Optional<Boolean> published
for every field।
Required data should remain required।
Common Mistake 6 — Optional Collection
Avoid:
Optional<List<Course>>
when:
List<Course>
with empty List naturally represents no results।
Common Mistake 7 — Hiding Exceptions as Empty
Do not catch every failure and return:
Optional.empty()
Absence এবং operational failure are different।
Common Mistake 8 — Meaningless Defaults
price.orElse(
0L
)
may hide difference between:
missing
and:
free
Choose fallback values carefully।
Common Mistake 9 — Nested Optional
If result becomes:
Optional<Optional<T>>
check whether:
flatMap()
is appropriate।
Common Mistake 10 — Using Optional Instead of a Domain State
Sometimes three or more meaningful states exist।
Instead of:
Optional<Boolean>
a domain enum may be clearer:
enum ReviewStatus {
APPROVED,
REJECTED,
NOT_REVIEWED
}
Practice 1 — Create Empty Optional
Solution
Optional<String> value =
Optional.empty();
Practice 2 — Known Non-Null Value
Solution
Optional<String> value =
Optional.of(
"Java"
);
Practice 3 — Nullable Value
Solution
Optional<String> value =
Optional.ofNullable(
input
);
Practice 4 — Transform Value
Given:
Optional<Course> course
get optional title।
Solution
Optional<String> title =
course.map(
Course::title
);
Practice 5 — Keep Only Published Course
Solution
Optional<Course> published =
course.filter(
Course::published
);
Practice 6 — Default String
Return:
Unknown
when title missing।
Solution
String title =
optionalTitle.orElse(
"Unknown"
);
Practice 7 — Lazy Fallback
Fallback is expensive।
Solution
String value =
optional.orElseGet(
() ->
buildFallback()
);
Practice 8 — Throw When Missing
Solution
Course course =
optional.orElseThrow(
() ->
new IllegalArgumentException(
"Course missing."
)
);
Practice 9 — map() or flatMap()
Given:
Course -> String
Use?
Answer
map()
Practice 10
Given:
Course -> Optional<Instructor>
Use?
Answer
flatMap()
Practice 11 — Empty Collection
Method may find zero Courses।
Return:
Optional<List<Course>>
or:
List<Course>
Answer
Usually:
List<Course>
and return empty List when none exist।
Practice 12 — Required Field
Every Course must have a code।
Should field type be:
Optional<String>
Answer
Usually no।
Make code required and validate:
Objects.requireNonNull(...)
plus other domain validation।
Practice 13 — orElse() vs orElseGet()
Which one lazily executes fallback logic?
Answer
orElseGet()
Practice 14 — Optional Lookup
Why is this signature useful?
Optional<Course> findByCode(
String code
);
Answer
Because method contract explicitly says:
matching Course may not exist
without relying on undocumented null behavior।
Practice 15 — Error Handling
Database connection fails while looking for a Course।
Should repository silently return:
Optional.empty()
Answer
Usually no।
That is an operational failure, not simply:
Course not found
Failure semantics should be preserved।
True or False
Optional<T>can represent a value or absence.Optional.of(null)producesOptional.empty().Optional.ofNullable(null)produces empty Optional.isEmpty()checks whether value is absent.get()is always the preferred Optional API.map()transforms a present value.filter()can turn a present Optional into empty.orElse()provides a fallback value.orElseGet()can create fallback lazily.orElseThrow()can convert absence into a meaningful exception.flatMap()is useful when mapping function returns Optional.- Every Java field should use Optional to avoid null.
- Empty List usually makes
Optional<List<T>>unnecessary. - Optional-returning methods should themselves return null when absent.
- Optional absence and operational failure mean the same thing.
Answers
1. True
2. False
3. True
4. True
5. False
6. True
7. True
8. True
9. True
10. True
11. True
12. False
13. True
14. False
15. False
Knowledge Check
Question 1
Optional<T> কী represent করে?
Question 2
Optional.of() এবং Optional.ofNullable()-এর difference কী?
Question 3
কেন Optional.get() blindly ব্যবহার করা উচিত নয়?
Question 4
Optional.map() কী করে?
Question 5
Optional.filter() কী করে?
Question 6
orElse() এবং orElseGet()-এর গুরুত্বপূর্ণ difference কী?
Question 7
orElseThrow() কখন useful?
Question 8
map() এবং flatMap()-এর difference কী?
Question 9
কেন Optional<List<T>> অনেক সময় unnecessary?
Question 10
কেন Optional required domain fields-এর replacement নয়?
Question 11
Optional absence এবং exception failure-এর difference কী?
Question 12
কোন ধরনের method return type হিসেবে Optional সবচেয়ে natural?
Knowledge Check Answers
Answer 1
Optional<T> explicitly represent করে যে একটি non-null value থাকতে পারে অথবা কোনো value নাও থাকতে পারে।
Answer 2
Optional.of(value) requires value non-null।
Optional.ofNullable(value) null হলে empty Optional তৈরি করে।
Answer 3
Optional empty হলে get() exception throw করে এবং absence handling intention clear করে না।
orElse(), orElseThrow(), map(), ifPresent()-এর মতো APIs সাধারণত intent better express করে।
Answer 4
Value present থাকলে mapping function apply করে এবং transformed value-কে Optional-এর মধ্যে রাখে।
Empty হলে empty-ই থাকে।
Answer 5
Present value condition satisfy করলে রাখে, otherwise empty Optional return করে।
Answer 6
orElse()-এর fallback expression eagerly evaluate হতে পারে।
orElseGet() Supplier ব্যবহার করে fallback only when Optional empty তখন তৈরি করে।
Answer 7
যখন current use case-এ value missing থাকা acceptable নয় এবং absence-কে meaningful exception-এ convert করতে চাই।
Answer 8
Mapper normal value return করলে:
map()
Mapper যদি:
Optional<R>
return করে, nested Optional avoid করতে:
flatMap()
use করা হয়।
Answer 9
List নিজেই zero elements represent করতে পারে।
No result-এর জন্য:
List.of()
সাধারণত sufficient।
Answer 10
যদি domain field অবশ্যই থাকতে হয়, correct solution হলো invariant enforce করা, যেমন:
Objects.requireNonNull(...)
Optional legitimate absence represent করে, invalid missing required data নয়।
Answer 11
Optional empty সাধারণত valid absence represent করে।
Exception operation failure, invalid state বা unexpected problem represent করতে পারে।
একটিকে অন্যটির মধ্যে silently convert করলে important information হারাতে পারে।
Answer 12
যে lookup/query method একটি single result খোঁজে এবং result না পাওয়া normal possibility।
Example:
Optional<Course> findByCode(
String code
);
Optional Decision Guide
Single lookup, may not exist:
Optional<Course>
Many results, may be zero:
List<Course>
Required value:
Course
plus validation বা exception।
Value exists but fallback needed:
orElse(...)
Expensive fallback:
orElseGet(...)
Missing should fail:
orElseThrow(...)
Transform present value:
map(...)
Transform using Optional-returning function:
flatMap(...)
Keep only if condition matches:
filter(...)
Optional Cheat Sheet
Empty
Optional.empty()
Known Non-Null
Optional.of(
value
)
Nullable Input
Optional.ofNullable(
value
)
Check Presence
optional.isPresent()
Check Absence
optional.isEmpty()
Run If Present
optional.ifPresent(
value ->
process(
value
)
);
Transform
optional.map(
value ->
transform(
value
)
);
Optional-Producing Transformation
optional.flatMap(
value ->
findSomething(
value
)
);
Filter
optional.filter(
value ->
condition
);
Default
optional.orElse(
fallback
);
Lazy Default
optional.orElseGet(
() ->
createFallback()
);
Fail if Missing
optional.orElseThrow(
() ->
new IllegalStateException(
"Required value missing."
)
);
Core Mental Model
Optional বুঝতে সবচেয়ে useful question:
Absence কি এই operation-এর একটি valid result?
If yes:
Optional<T>
useful হতে পারে।
Example:
Find Course by code
→ Course থাকতে পারে
→ না-ও থাকতে পারে
But:
Create a valid Course
এখানে required code missing হওয়া legitimate state নয়।
তাই:
Optional code
না বানিয়ে invariant enforce করুন।
আর Optional chain পড়ুন এভাবে:
findByCode(
code
)
.filter(
Course::published
)
.map(
Course::title
)
.orElse(
"Unavailable"
);
Meaning:
Course খুঁজো
↓
পেলে published কি না দেখো
↓
published হলে title নাও
↓
কিছু না থাকলে fallback দাও
Lesson Summary
এই lesson-এ আমরা Java-তে modern null handling এবং Optional<T>-এর foundation শিখেছি।
আমরা শিখেছি:
nullmissing value represent করতে পারে, কিন্তু API contract ambiguous করতে পারেOptional<T>presence বা absence explicitly represent করেOptional.empty()absence represent করেOptional.of()known non-null value-এর জন্যOptional.ofNullable()nullable source value handle করতে পারেisPresent()এবংisEmpty()state check করেget()blindly ব্যবহার করা unsafeifPresent()value থাকলে action চালায়ifPresentOrElse()presence এবং absence দুইটি branch handle করতে পারেmap()contained value transform করেfilter()condition না মিললে Optional empty করতে পারেflatMap()Optional-returning transformation flatten করেorElse()fallback value দেয়orElseGet()lazy fallback দেয়orElseThrow()absence-কে meaningful failure-এ convert করতে পারেor()alternative Optional source try করতে পারে- Optional return type single-result lookup-এর জন্য natural
- Optional required domain fields-এর replacement নয়
- Optional parameter বা field হিসেবে blindly ব্যবহার করা উচিত নয়
- Empty collection সাধারণত
Optional<List<T>>-এর চেয়ে cleaner - Optional-returning method কখনো
nullreturn করা উচিত নয় - Absence এবং operational failure আলাদা concepts
- Domain invariants যত early সম্ভব enforce করা ভালো
- Default value ব্যবহার করার আগে semantic information হারাচ্ছে কি না ভাবতে হবে
সবচেয়ে গুরুত্বপূর্ণ principle:
Optional-এর কাজ null লুকানো নয়।
Optional-এর কাজ meaningful absence
explicit করা।
আর practical rule:
May exist
→ Optional
Must exist
→ required value
Many results
→ collection, possibly empty
Operation failed
→ proper failure handling
Next Lesson
পরবর্তী lesson:
The Modern Date and Time API
আমরা শিখব:
- কেন old date/time APIs problematic ছিল
LocalDateLocalTimeLocalDateTimeInstantDurationPeriodZoneIdZonedDateTimeDateTimeFormatter- Date arithmetic
- Time-zone conversion
LocalDateTimeকেন absolute moment নয়Instantকখন ব্যবহার করা উচিত- User-facing local time এবং system timestamps
- Common date/time mistakes