Modern Java
Lambda Expressions
You are viewing a free preview lesson.
Lesson Overview
Modern Java-তে Lambda Expression খুব গুরুত্বপূর্ণ একটি feature।
Lambda ব্যবহার করে আমরা ছোট একটি behavior বা logic-কে conciseভাবে প্রকাশ করতে পারি এবং প্রয়োজন হলে সেই behavior-কে অন্য method-এর কাছে pass করতে পারি।
Example:
number -> number * 2
আরেকটি example:
course ->
course.priceInPaisa()
> 500_000
প্রথমবার Lambda syntax দেখলে কিছুটা অস্বাভাবিক মনে হতে পারে।
কিন্তু এর মূল idea খুব simple:
Lambda Expression
→ একটি behavior-এর concise implementation
এই lesson-এ আমরা শিখব:
- কেন Lambda Expression প্রয়োজন
- Lambda কী represent করে
- Lambda syntax
- Single parameter Lambda
- Multiple parameter Lambda
- No-parameter Lambda
- Expression Lambda
- Block Lambda
- Return value
- Type inference
- Lambda এবং
Comparator - Collection APIs-এর সাথে Lambda
- Behavior parameter হিসেবে pass করা
- Variable capture
effectively final- Lambda scope
- Side effects
- কখন Lambda code পরিষ্কার করে
- কখন normal method বা loop better
Why Do We Need Lambda Expressions?
ধরুন আমাদের একটি Course model আছে:
record Course(
String title,
long priceInPaisa
) {
}
এখন আমরা Course-গুলো price অনুযায়ী sort করতে চাই।
Java-তে Comparator<Course> ব্যবহার করে sorting rule define করা যায়।
Lambda আসার আগে common approach ছিল anonymous class ব্যবহার করা।
Comparator<Course> byPrice =
new Comparator<Course>() {
@Override
public int compare(
Course first,
Course second
) {
return Long.compare(
first.priceInPaisa(),
second.priceInPaisa()
);
}
};
তারপর:
courses.sort(
byPrice
);
Code completely valid।
কিন্তু actual business logic কতটুকু?
মূল logic শুধু:
Long.compare(
first.priceInPaisa(),
second.priceInPaisa()
);
বাকি অংশ mainly ceremony:
new Comparator<Course>()
anonymous class
@Override
compare() declaration
ছোট একটি behavior প্রকাশ করতে অনেক boilerplate লিখতে হচ্ছে।
Lambda এই boilerplate কমায়।
Same Comparator with Lambda
একই behavior Lambda দিয়ে:
Comparator<Course> byPrice =
(
first,
second
) -> Long.compare(
first.priceInPaisa(),
second.priceInPaisa()
);
এখানে code-এর focus সরাসরি চলে এসেছে:
দুইটি Course কীভাবে compare হবে?
এই behavior-এর উপর।
What Is a Lambda Expression?
Lambda Expression হলো এমন একটি concise syntax যার মাধ্যমে compatible Functional Interface-এর behavior implement করা যায়।
সহজভাবে:
Input
↓
Behavior
↓
Result
Example:
number -> number * 2
এখানে:
Input:
number
Behavior:
number * 2
Result:
doubled number
Lambda Is Not a Standalone Function
Java-তে Lambda Python বা JavaScript-এর standalone function-এর মতো independently exist করে না।
Lambda-এর একটি:
target type
লাগে।
Example:
Comparator<Integer> comparator =
(
first,
second
) -> Integer.compare(
first,
second
);
এখানে target type:
Comparator<Integer>
Java Comparator দেখে বুঝতে পারে Lambda-টি কোন behavior implement করছে।
Functional Interface Preview
ধরুন আমরা লিখলাম:
@FunctionalInterface
interface Calculator {
int calculate(
int first,
int second
);
}
এখানে abstract method একটি:
calculate(...)
তাই Lambda ব্যবহার করে এর implementation দিতে পারি:
Calculator addition =
(
first,
second
) -> first + second;
এখন:
int result =
addition.calculate(
10,
20
);
Result:
30
Functional Interface আমরা পরের lesson-এ বিস্তারিত শিখব।
এই lesson-এর জন্য শুধু মনে রাখুন:
Lambda
→ Functional Interface-এর behavior implement করে
Basic Lambda Syntax
Lambda-এর basic structure:
(parameters) -> expression
Example:
(
first,
second
) -> first + second
আর multiple statements থাকলে:
(parameters) -> {
statements
}
Example:
(
first,
second
) -> {
int result =
first + second;
return result;
}
The Arrow Operator
Lambda syntax-এর:
->
অংশটি parameters এবং behavior আলাদা করে।
Conceptually:
input
->
what to do with the input
Example:
name -> name.length()
পড়তে পারেন:
একটি name নাও
তার length return করো
Single Parameter Lambda
একটি parameter থাকলে:
name -> name.length()
লেখা যায়।
এটিও valid:
(name) -> name.length()
একটি inferred parameter-এর ক্ষেত্রে parentheses optional।
Another Single Parameter Example
number -> number > 0
Meaning:
একটি number নাও
number positive কি না return করো
Multiple Parameters
দুই বা তার বেশি parameters থাকলে parentheses লাগবে।
Example:
(
first,
second
) -> first + second
Incorrect:
first, second -> first + second
No Parameters
কোনো parameter না থাকলে empty parentheses ব্যবহার করতে হয়।
() -> System.out.println(
"Started"
)
আরেকটি example:
() -> new ArrayList<>()
Expression Lambda
একটি Lambda যদি শুধু একটি expression return করে, তাহলে braces এবং return প্রয়োজন হয় না।
Example:
number -> number * 2
এখানে result automatically:
number * 2
Another Expression Lambda
course ->
course.priceInPaisa()
> 0
এটি একটি boolean expression।
Result হবে:
true
অথবা:
false
Block Lambda
একাধিক statement প্রয়োজন হলে braces ব্যবহার করতে হয়।
number -> {
int doubled =
number * 2;
System.out.println(
doubled
);
return doubled;
}
এটিকে বলা যায়:
Block Lambda
Return from a Block Lambda
যদি block Lambda-এর target interface result expect করে, তাহলে explicit return প্রয়োজন।
Correct:
(
first,
second
) -> {
int result =
first + second;
return result;
}
Incorrect:
(
first,
second
) -> {
int result =
first + second;
}
যদি return value expected হয়, দ্বিতীয় version compile করবে না।
Expression Lambda vs Block Lambda
Simple behavior হলে:
number -> number * 2
prefer করা যায়।
Unnecessarily:
number -> {
return number * 2;
}
লিখলে code বড় হয়, কিন্তু clarity বাড়ে না।
Void Lambda
সব Lambda value return করে না।
Example:
message ->
System.out.println(
message
)
এখানে behavior হলো:
message print করা
Meaningful return value নেই।
Multiple Statement Void Lambda
message -> {
System.out.println(
"Message received"
);
System.out.println(
message
);
}
এখানে return দরকার নেই।
Type Inference
Lambda parameters-এর type অনেক সময় explicitly লিখতে হয় না।
Example:
Comparator<String> byLength =
(
first,
second
) -> Integer.compare(
first.length(),
second.length()
);
আমরা লিখিনি:
String first
String second
তবুও Java জানে এগুলো String।
কেন?
Target type:
Comparator<String>
Explicit Parameter Types
চাইলে type explicitly লেখা যায়:
Comparator<String> byLength =
(
String first,
String second
) -> Integer.compare(
first.length(),
second.length()
);
কিন্তু type obvious হলে inferred version সাধারণত cleaner।
Do Not Mix Parameter Styles
এভাবে লেখা যাবে না:
(
String first,
second
) -> ...
Either:
(
String first,
String second
) -> ...
or:
(
first,
second
) -> ...
Lambda with Comparator
Comparator Lambda-এর খুব common use case।
Suppose:
List<Integer> numbers =
new ArrayList<>(
List.of(
30,
10,
20
)
);
Ascending sort:
numbers.sort(
(
first,
second
) -> Integer.compare(
first,
second
)
);
Result:
10
20
30
Descending Comparator
numbers.sort(
(
first,
second
) -> Integer.compare(
second,
first
)
);
Result:
30
20
10
Lambda with Custom Objects
Suppose:
record Course(
String title,
long priceInPaisa
) {
}
Sort by price:
courses.sort(
(
first,
second
) -> Long.compare(
first.priceInPaisa(),
second.priceInPaisa()
)
);
Standard Comparator API Can Be Better
উপরের Lambda valid।
কিন্তু Java already provides:
Comparator.comparingLong(
Course::priceInPaisa
)
So production code-এ:
courses.sort(
Comparator.comparingLong(
Course::priceInPaisa
)
);
আরও expressive হতে পারে।
Important principle:
Lambda জানি বলে সবকিছু Lambda দিয়ে লিখতে হবে না।
Clearer standard API থাকলে সেটি ব্যবহার করুন।
Lambda with removeIf()
Suppose:
List<Integer> scores =
new ArrayList<>(
List.of(
40,
85,
30,
95,
70
)
);
আমরা 50-এর কম scores remove করতে চাই।
scores.removeIf(
score -> score < 50
);
Result:
85
95
70
Reading the Lambda
এই Lambda:
score -> score < 50
এভাবে পড়তে পারেন:
একটি score নাও।
score যদি 50-এর কম হয়,
true return করো।
removeIf() true পাওয়া elements remove করবে।
Lambda with forEach()
Suppose:
List<String> names =
List.of(
"Sakib",
"Subu",
"Sumu"
);
Print:
names.forEach(
name ->
System.out.println(
name
)
);
Equivalent Traditional Loop
একই কাজ:
for (
String name
: names
) {
System.out.println(
name
);
}
দুইটিই valid।
Lambda Does Not Automatically Mean Better Code
Simple operation-এর ক্ষেত্রে:
names.forEach(
name ->
System.out.println(
name
)
);
clean হতে পারে।
কিন্তু complex control flow হলে traditional loop clearer হতে পারে।
Example requirements:
condition check
continue
break
multiple state changes
এগুলো normal loop-এ অনেক সময় সহজে বোঝা যায়।
Behavior as a Parameter
Lambda-এর সবচেয়ে powerful conceptগুলোর একটি:
method-এর কাছে শুধু data নয়,
behavior-ও pass করা যায়।
Normally আমরা লিখি:
process(
course
);
এখানে course data।
কিন্তু Lambda-এর মাধ্যমে আমরা বলতে পারি:
data নিয়ে কী করা হবে
Custom Behavior Example
Define:
@FunctionalInterface
interface NumberOperation {
int apply(
int first,
int second
);
}
Then:
NumberOperation addition =
(
first,
second
) -> first + second;
আরেকটি:
NumberOperation multiplication =
(
first,
second
) -> first * second;
Same Interface, Different Behavior
Use:
int sum =
addition.apply(
10,
5
);
Result:
15
And:
int product =
multiplication.apply(
10,
5
);
Result:
50
একই interface।
Behavior আলাদা।
Passing the Behavior Directly
আমরা method বানাতে পারি:
static int calculate(
int first,
int second,
NumberOperation operation
) {
return operation.apply(
first,
second
);
}
Call:
int result =
calculate(
10,
5,
(
first,
second
) -> first + second
);
Another Call
int result =
calculate(
10,
5,
(
first,
second
) -> first * second
);
calculate() method একই।
Caller decide করছে:
কোন behavior ব্যবহার হবে।
Why Is This Useful?
Without behavior parameterization, আমরা হয়তো লিখতাম:
calculateAddition(...)
calculateMultiplication(...)
calculateMaximum(...)
calculateMinimum(...)
কিছু situations-এ একটি common operation-এর variation behavior হিসেবে pass করা cleaner design দিতে পারে।
But Do Not Over-Abstract
Lambda support আছে বলে এমন method বানানোর দরকার নেই:
method
+ 5 behavior parameters
+ 4 callbacks
+ unclear responsibility
এতে code flexible হলেও difficult to understand হতে পারে।
Use behavior parameterization when variation genuinely meaningful।
Lambda and Object-Oriented Programming
Lambda OOP replace করে না।
Modern Java-তে আমরা একসঙ্গে ব্যবহার করি:
Classes
Objects
Interfaces
Composition
Lambdas
Functional APIs
Lambda mainly small behavior express করা সহজ করে।
Variable Capture
Lambda নিজের বাইরের scope-এর কিছু local variable ব্যবহার করতে পারে।
Example:
int minimumScore =
80;
scores.removeIf(
score ->
score
< minimumScore
);
এখানে Lambda use করছে:
minimumScore
যেটি Lambda-এর বাইরে declared।
এটিকে বলা হয়:
Variable Capture
Captured Variable
Example:
int threshold =
100;
Predicate<Integer> rule =
number ->
number > threshold;
এখানে Lambda capture করছে:
threshold
Final or Effectively Final
Lambda local variable capture করতে পারবে যদি variable:
final
অথবা:
effectively final
হয়।
Explicit final
final int minimumScore =
80;
scores.removeIf(
score ->
score
< minimumScore
);
Valid।
Effectively Final
এটিও valid:
int minimumScore =
80;
scores.removeIf(
score ->
score
< minimumScore
);
আমরা final keyword লিখিনি।
কিন্তু minimumScore পরে reassign করিনি।
তাই variableটি:
effectively final
What Does Effectively Final Mean?
সহজভাবে:
Variable একবার value পেয়েছে
এবং পরে নতুন value assign করা হয়নি।
Example:
int limit =
10;
যদি আর কোথাও না লিখি:
limit =
somethingElse;
তাহলে limit effectively final।
Invalid Capture
int minimumScore =
80;
minimumScore =
90;
scores.removeIf(
score ->
score
< minimumScore
);
এটি compile করবে না।
কারণ:
minimumScore
reassigned হয়েছে।
এটি effectively final নয়।
Another Invalid Example
int count =
0;
names.forEach(
name ->
count++
);
এটিও compile করবে না।
কারণ Lambda captured local variable:
count
modify করছে।
Why Does Java Restrict Captured Locals?
এই restriction code reasoning সহজ রাখে।
Local variables method execution-এর সাথে tightly connected।
যদি Lambdas freely local variables mutate করতে পারত, তাহলে:
variable lifetime
concurrency
visibility
shared mutable state
reason করা আরও complicated হতো।
Java তাই captured local variable-এর value stable রাখে।
Mutating an Object Is Different
Consider:
List<String> result =
new ArrayList<>();
names.forEach(
name ->
result.add(
name
)
);
এটি compile করে।
কেন?
কারণ local variable:
result
নতুন object-এ reassign হয়নি।
Reference একই আছে।
কিন্তু referenced ArrayList object mutate হয়েছে।
Reference Reassignment vs Object Mutation
এইটি:
result.add(
value
);
object mutate করে।
এইটি:
result =
new ArrayList<>();
local variable reference reassign করে।
Lambda capture-এর জন্য এই দুইটি একই বিষয় নয়।
Does Compiling Mean Good Design?
না।
এই code valid:
List<Course> result =
new ArrayList<>();
courses.forEach(
course -> {
if (
course.priceInPaisa()
> 0
) {
result.add(
course
);
}
}
);
কিন্তু traditional loop হয়তো clearer:
for (
Course course
: courses
) {
if (
course.priceInPaisa()
> 0
) {
result.add(
course
);
}
}
Later আমরা Stream API শিখব, যেখানে transformation-এর আরও natural functional style দেখব।
Instance Fields and Lambda
Instance field captured local variable নয়।
Example:
class Counter {
private int count;
void process(
List<String> names
) {
names.forEach(
name ->
count++
);
}
}
এটি compile করতে পারে।
কিন্তু shared mutable state এবং concurrency থাকলে এটি thread-safety issue তৈরি করতে পারে।
Concurrency lessons-এ আমরা এটি বিস্তারিত দেখব।
Lambda Scope
Lambda parameter Lambda body-এর ভিতরে available।
Example:
course ->
course.title()
এখানে:
course
শুধু Lambda-এর parameter।
Parameter Naming
Simple example:
x -> x * 2
ঠিক আছে।
কিন্তু business code-এ meaningful names prefer করুন।
Better:
course ->
course.priceInPaisa()
> 0
than:
c ->
c.priceInPaisa()
> 0
যদিও short Lambdas-এ concise naming acceptable হতে পারে।
Lambda and this
Lambda নিজের আলাদা this introduce করে না।
Instance method-এর ভিতরে Lambda ব্যবহার করলে:
this
সাধারণত enclosing object-কে refer করে।
এটি anonymous class-এর behavior থেকে different।
Lambda vs Anonymous Class
Anonymous class:
Comparator<Course> byPrice =
new Comparator<Course>() {
@Override
public int compare(
Course first,
Course second
) {
return Long.compare(
first.priceInPaisa(),
second.priceInPaisa()
);
}
};
Lambda:
Comparator<Course> byPrice =
(
first,
second
) -> Long.compare(
first.priceInPaisa(),
second.priceInPaisa()
);
ছোট functional behavior-এর জন্য Lambda অনেক concise।
But Lambda Is Not Just Short Anonymous-Class Syntax
দুইটির মধ্যে differences আছে:
this semantics
scope behavior
runtime representation
তাই Lambda-কে শুধু:
anonymous class-এর shortcut
হিসেবে ভাবা পুরোপুরি accurate নয়।
Better mental model:
Functional Interface-এর behavior implementation
Target Typing
Lambda-এর type context থেকে আসে।
Suppose:
Function<Integer, Integer> doubler =
number ->
number * 2;
Target type বলছে:
Input:
Integer
Output:
Integer
এই context ছাড়া:
number -> number * 2
এর exact Java type জানা সম্ভব নয়।
Same Shape, Different Meaning
এই Lambda:
value -> value
different contexts-এ হতে পারে:
String → String
Integer → Integer
Course → Course
Target interface determine করবে actual type।
Lambda with Validation
Lambda body-তে normal Java logic ব্যবহার করা যায়।
Example:
value -> {
if (
value < 0
) {
throw new IllegalArgumentException(
"Value cannot be negative."
);
}
return value * 2;
}
Lambda ব্যবহার করছি বলে normal exception rules disappear করে না।
Keep Lambdas Small
Good Lambda:
course ->
course.priceInPaisa()
> 500_000
এক নজরেই intent বোঝা যায়।
Large Lambda
এমন code consider করুন:
course -> {
validateCourse(
course
);
calculatePrice(
course
);
updateMetrics(
course
);
sendNotifications(
course
);
writeAuditLog(
course
);
}
Technically possible।
কিন্তু behavior বড় হয়ে যাচ্ছে।
এখন named method clearer হতে পারে:
courses.forEach(
course ->
processCourse(
course
)
);
Extract Meaningful Methods
Instead of:
courses.removeIf(
course ->
course.priceInPaisa()
== 0
&& course.title()
.length()
< 5
&& ...
);
যদি এটি একটি meaningful business rule হয়:
courses.removeIf(
course ->
shouldRemoveCourse(
course
)
);
আরও readable হতে পারে।
Business Logic Deserves Names
Suppose একটি Course promotion-এর জন্য eligible কি না determine করতে অনেক rules লাগে।
Long inline Lambda না লিখে:
boolean isEligibleForPromotion(
Course course
)
method create করা better হতে পারে।
Then:
course ->
isEligibleForPromotion(
course
)
নিজেই meaningful।
Side Effects
Lambda সবসময় pure হতে হবে না।
Example:
name ->
System.out.println(
name
)
print করা একটি:
side effect
আরও side effects:
Collection mutate করা
Field update করা
File লেখা
Network request করা
Logging করা
Pure Lambda
Example:
number ->
number * 2
এটি input নেয় এবং result return করে।
External state change করে না।
এ ধরনের behavior generally সহজে reason করা যায়।
Side-Effecting Lambda
course -> {
auditLog.add(
course.code()
);
return course.priceInPaisa()
> 0;
}
এখানে Lambda দুইটি কাজ করছে:
audit log change করছে
এবং
boolean decision return করছে
এ ধরনের mixed responsibility carefully use করতে হবে।
Lambdas Do Not Replace Loops
Suppose আমরা প্রথম invalid Course খুঁজতে চাই এবং পাওয়ার সাথে সাথে loop stop করতে চাই।
Traditional loop:
for (
Course course
: courses
) {
if (
!isValid(
course
)
) {
System.out.println(
"Invalid course: "
+ course.title()
);
break;
}
}
এটি খুব clear।
একই logic জোর করে:
forEach(...)
দিয়ে করা code-কে unnecessarily complicated করতে পারে।
forEach() and Control Flow
Normal loop-এর ভিতরে আমরা ব্যবহার করতে পারি:
break
continue
forEach() Lambda-এর ভিতরে এগুলো একইভাবে ব্যবহার করা যায় না।
তাই:
Lambda syntax shorter
মানেই:
Lambda always better
না।
Example — Sorting by String Length
List<String> titles =
new ArrayList<>(
List.of(
"Java",
"System Design",
"Backend"
)
);
titles.sort(
(
first,
second
) -> Integer.compare(
first.length(),
second.length()
)
);
Result:
Java
Backend
System Design
Add a Secondary Rule
Suppose equal length হলে alphabetical order চাই।
titles.sort(
(
first,
second
) -> {
int byLength =
Integer.compare(
first.length(),
second.length()
);
if (
byLength != 0
) {
return byLength;
}
return first.compareTo(
second
);
}
);
এটি valid।
Comparator API Is Clearer Here
Same idea:
titles.sort(
Comparator.comparingInt(
String::length
).thenComparing(
Comparator.naturalOrder()
)
);
এখানে standard API intent আরও clearly communicate করছে।
Custom Functional Interface Example
@FunctionalInterface
interface CourseRule {
boolean test(
Course course
);
}
Then:
CourseRule paidCourse =
course ->
course.priceInPaisa()
> 0;
আরেকটি:
CourseRule expensiveCourse =
course ->
course.priceInPaisa()
>= 500_000;
Applying the Behavior
static boolean matches(
Course course,
CourseRule rule
) {
return rule.test(
course
);
}
Call:
boolean result =
matches(
course,
c ->
c.priceInPaisa()
> 0
);
Standard Functional Interfaces
Java already provides common functional interfaces।
Examples:
Predicate<T>
Function<T, R>
Consumer<T>
Supplier<T>
তাই সব behavior-এর জন্য custom interface বানাতে হয় না।
পরের lesson-এ এগুলো বিস্তারিত শিখব।
Practical Example — Course Sorting
import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<Course> courses =
new ArrayList<>(
List.of(
new Course(
"Java Foundation",
300_000
),
new Course(
"System Design",
800_000
),
new Course(
"Backend Development",
500_000
)
)
);
courses.sort(
(
first,
second
) -> Long.compare(
first.priceInPaisa(),
second.priceInPaisa()
)
);
courses.forEach(
course ->
System.out.println(
course.title()
+ " - "
+ course.priceInPaisa()
)
);
}
record Course(
String title,
long priceInPaisa
) {
}
}
Output:
Java Foundation - 300000
Backend Development - 500000
System Design - 800000
Practical Example — Passing Behavior
public class Main {
public static void main(String[] args) {
int addition =
calculate(
10,
5,
(
first,
second
) -> first + second
);
int multiplication =
calculate(
10,
5,
(
first,
second
) -> first * second
);
System.out.println(
addition
);
System.out.println(
multiplication
);
}
static int calculate(
int first,
int second,
NumberOperation operation
) {
return operation.apply(
first,
second
);
}
@FunctionalInterface
interface NumberOperation {
int apply(
int first,
int second
);
}
}
Output:
15
50
Practice 1 — Lambda Parameter
Given:
number -> number * 2
Parameter কোনটি?
Answer
number
Practice 2 — Behavior
name -> name.length()
এই Lambda কী করে?
Answer
একটি name নেয় এবং তার length return করে।
Practice 3 — Two Parameters
দুইটি integer যোগ করার Lambda লিখুন।
Solution
(
first,
second
) -> first + second
Practice 4 — No Parameters
Started print করার Lambda লিখুন।
Solution
() ->
System.out.println(
"Started"
)
Practice 5 — Block Lambda
এই Lambda:
number -> number * 2
block form-এ লিখুন।
Solution
number -> {
return number * 2;
}
Practice 6 — Comparator
Ascending integer comparator Lambda:
Solution
(
first,
second
) -> Integer.compare(
first,
second
)
Practice 7 — Descending Comparator
Solution
(
first,
second
) -> Integer.compare(
second,
first
)
Practice 8 — removeIf()
Length 4-এর কম String remove করুন।
Solution
values.removeIf(
value ->
value.length()
< 4
);
Practice 9 — Variable Capture
Valid?
int minimum =
50;
scores.removeIf(
score ->
score
< minimum
);
Answer
Yes।
minimum effectively final।
Practice 10 — Invalid Capture
Valid?
int minimum =
50;
minimum =
60;
scores.removeIf(
score ->
score
< minimum
);
Answer
No।
minimum reassigned হয়েছে, তাই effectively final নয়।
Practice 11 — Object Mutation
এটি compile করবে?
List<Integer> result =
new ArrayList<>();
values.forEach(
value ->
result.add(
value
)
);
Answer
Yes।
result reference reassign করা হয়নি।
Referenced ArrayList mutate হয়েছে।
Practice 12 — Large Lambda
একটি Lambda-এর ভিতরে আছে:
Validation
Logging
Multiple branches
Error handling
Database-like operations
এটি inline রাখা উচিত?
Answer
সাধারণত না।
Meaningful named method বা abstraction extract করা clearer।
Practice 13 — Target Type
এই code-এ Java কীভাবে জানে first এবং second হলো Course?
Comparator<Course> comparator =
(
first,
second
) -> ...
Answer
Target type:
Comparator<Course>
Lambda parameter types determine করতে compiler-কে context দেয়।
Practice 14 — Loop or Lambda?
Requirement:
First invalid element পেলে processing stop করতে হবে।
কোনটি সাধারণত clearer?
Answer
Traditional for loop।
কারণ:
break
naturalভাবে ব্যবহার করা যায়।
True or False
- Lambda Expression behavior represent করতে পারে।
- Lambda কোনো target type ছাড়াই standalone Java function।
- একটি parameter হলে parentheses অনেক সময় omit করা যায়।
- Multiple parameters হলে parentheses লাগে।
- Expression Lambda explicit
returnছাড়াই result দিতে পারে। - Block Lambda result দিলে explicit
returnলাগতে পারে। - Java target type থেকে Lambda parameter type infer করতে পারে।
- Captured local variable freely reassign করা যায়।
- Captured local variable final বা effectively final হতে হয়।
- Effectively-final reference-এর object mutate করা সম্ভব।
- সব traditional loop Lambda দিয়ে replace করা উচিত।
- বড় business logic inline Lambda-তে রাখাই best।
ComparatorLambda-এর common use case।- Lambda OOP replace করে।
- Clearer standard API থাকলে Lambda manually লেখা বাধ্যতামূলক নয়।
Answers
1. True
2. False
3. True
4. True
5. True
6. True
7. True
8. False
9. True
10. True
11. False
12. False
13. True
14. False
15. True
Knowledge Check
Question 1
Lambda Expression কী?
Question 2
Lambda কেন useful?
Question 3
-> কী separate করে?
Question 4
Expression Lambda এবং Block Lambda-এর difference কী?
Question 5
Java কীভাবে Lambda parameter-এর type infer করে?
Question 6
Lambda-এর target type কেন প্রয়োজন?
Question 7
Variable capture কী?
Question 8
effectively final বলতে কী বোঝায়?
Question 9
Captured local variable কেন reassign করা যায় না?
Question 10
কেন একটি captured ArrayList mutate করা যায় কিন্তু local reference reassign করা যায় না?
Question 11
কখন Lambda-এর বদলে normal method better?
Question 12
কখন traditional loop Lambda-based forEach()-এর চেয়ে better?
Knowledge Check Answers
Answer 1
Lambda Expression হলো compatible Functional Interface-এর একটি behavior conciseভাবে implement করার syntax।
Example:
number ->
number * 2
Answer 2
Lambda ছোট behavior-এর জন্য anonymous class-এর boilerplate কমায় এবং behavior-কে method-এর কাছে parameter হিসেবে pass করা সহজ করে।
Answer 3
->
Lambda parameters এবং Lambda body বা behavior আলাদা করে।
Answer 4
Expression Lambda একটি expression দিয়ে result দিতে পারে:
number ->
number * 2
Block Lambda braces-এর মধ্যে multiple statements রাখতে পারে:
number -> {
int result =
number * 2;
return result;
}
Answer 5
Expected Functional Interface বা target type compiler-কে parameter এবং return type সম্পর্কে information দেয়।
Answer 6
Lambda নিজে standalone named type নয়। কোন functional behavior implement হচ্ছে তা target interface determine করে।
Answer 7
Lambda যখন নিজের বাইরের local variable ব্যবহার করে, তাকে variable capture বলা হয়।
Example:
int limit =
10;
value ->
value > limit
Answer 8
একটি local variable initial value পাওয়ার পরে যদি আর reassign না হয়, তাহলে explicit final না থাকলেও সেটি effectively final।
Answer 9
Java captured local variables-এর value stable রাখে, যাতে scope, state এবং concurrency reasoning manageable থাকে।
Answer 10
কারণ local reference একই object-কে point করছে, তাই reference effectively final থাকতে পারে। Object-এর internal state পরিবর্তন করা reference reassign করার সমান নয়।
Answer 11
যখন behavior:
দীর্ঘ
multiple branches আছে
important business rule represent করে
error handling বেশি
অনেক side effect আছে
তখন named method clearer হতে পারে।
Answer 12
যখন:
break
continue
early exit
complex mutable state
প্রয়োজন হয়, traditional loop সাধারণত clearer।
Lambda Syntax Cheat Sheet
No Parameters
() ->
doSomething()
One Parameter
value ->
process(
value
)
Multiple Parameters
(
first,
second
) ->
first + second
Expression Returning a Value
value ->
value * 2
Block Returning a Value
value -> {
int result =
value * 2;
return result;
}
Void Behavior
value ->
System.out.println(
value
)
Practical Lambda Checklist
Lambda লেখার আগে নিজেকে জিজ্ঞেস করুন:
কোন Functional Interface expected?
Lambda কী input নিচ্ছে?
কী result দিচ্ছে?
একটি expression যথেষ্ট কি?
কোন external variable capture করছি?
Captured local variable effectively final কি?
Lambda unnecessary side effect করছে কি?
Lambda body খুব বড় হয়ে যাচ্ছে কি?
Named method intent আরও ভালোভাবে explain করবে কি?
Java standard library-তে clearer helper already আছে কি?
Core Mental Model
Lambda-কে শুধু:
short syntax
হিসেবে ভাববেন না।
Better mental model:
একটি method কিছু data পেয়েছে।
এখন caller সেই method-কে
কীভাবে কাজ করতে হবে
সেই behavior-টিও দিতে পারে।
Example questions:
এই Course-গুলো কোন rule অনুযায়ী sort হবে?
কোন Course remove হবে?
প্রতিটি item নিয়ে কী করা হবে?
দুইটি number কীভাবে combine হবে?
Lambda সেই:
behavior
provide করে।
Lesson Summary
এই lesson-এ আমরা Lambda Expression-এর foundation শিখেছি।
আমরা শিখেছি:
- Lambda ছোট behavior conciseভাবে express করে
- Lambda একটি compatible
Functional Interface-এর behavior implement করে - Lambda standalone Java function নয়
- Lambda-এর একটি target type প্রয়োজন
->parameters এবং behavior আলাদা করে- একটি parameter-এর parentheses optional হতে পারে
- Multiple parameters-এর জন্য parentheses লাগে
- No-parameter Lambda
()ব্যবহার করে - Expression Lambda conciseভাবে result return করতে পারে
- Block Lambda multiple statements support করে
- Java target type থেকে parameter type infer করতে পারে
- Lambda
Comparator-এর সাথে খুব commonly used হয় removeIf()এবংforEach()Lambda behavior নিতে পারে- Behavior method parameter হিসেবে pass করা যায়
- Lambda OOP-এর complement, replacement নয়
- Lambda বাইরের local variable capture করতে পারে
- Captured local variable final বা effectively final হতে হয়
- Object mutation এবং local reference reassignment একই বিষয় নয়
- Side effects Lambda-এর ভিতরে possible, কিন্তু intentionally ব্যবহার করা উচিত
- Lambda ছোট এবং focused রাখা ভালো
- Complex business logic named method-এ রাখা clearer হতে পারে
- Traditional loops এখনো অনেক situation-এ better
- Standard Java APIs clearer হলে unnecessary manual Lambda এড়িয়ে যাওয়া উচিত
সবচেয়ে গুরুত্বপূর্ণ idea:
আগে আমরা method-এর কাছে data pass করতাম।
Modern Java-তে functional interfaces-এর মাধ্যমে
behavior-ও pass করতে পারি।
আর Lambda Expression সেই behavior লেখাকে concise এবং practical করে।
Next Lesson
পরবর্তী lesson:
Functional Interfaces and Method References
আমরা শিখব:
- Functional Interface কী
@FunctionalInterfacePredicate<T>Function<T, R>Consumer<T>Supplier<T>UnaryOperator<T>BinaryOperator<T>- Behavior composition
Method ReferenceClassName::staticMethodobject::instanceMethodClassName::instanceMethod- Constructor references
- Lambda এবং Method Reference-এর মধ্যে practical choice