Modern Java
Functional Interfaces and Method References
You are viewing a free preview lesson.
Lesson Overview
আগের lesson-এ আমরা Lambda Expression শিখেছি।
আমরা দেখেছি, ছোট একটি behavior-কে inlineভাবে লিখে কোনো method-এর কাছে pass করা যায়।
Example:
number -> number * 2
বা:
course ->
course.priceInPaisa()
> 500_000
কিন্তু একটি গুরুত্বপূর্ণ প্রশ্ন এখনো বাকি:
Java কীভাবে বুঝে একটি Lambda কী ধরনের behavior represent করছে?
উত্তর:
Functional Interface
Lambda Expression নিজে কোনো standalone function নয়।
একটি Lambda সবসময় কোনো compatible Functional Interface-এর implementation হিসেবে কাজ করে।
Modern Java-তে কিছু standard functional interfaces আছে যেগুলো আমরা খুব frequently ব্যবহার করি:
Predicate<T>
Function<T, R>
Consumer<T>
Supplier<T>
UnaryOperator<T>
BinaryOperator<T>
এই lesson-এ আমরা শিখব:
Functional Interfaceকী@FunctionalInterfacePredicate<T>Function<T, R>Consumer<T>Supplier<T>UnaryOperator<T>BinaryOperator<T>- Custom functional interfaces
- Behavior composition
and(),or(),negate()andThen()এবংcompose()Method Reference- Static method reference
- Bound instance method reference
- Unbound instance method reference
- Constructor reference
- Lambda বনাম Method Reference
- কখন কোনটি clearer
What Is a Functional Interface?
Functional Interface হলো এমন একটি interface যেখানে একটি মাত্র abstract method থাকে।
Example:
@FunctionalInterface
interface Calculator {
int calculate(
int first,
int second
);
}
এখানে abstract method মাত্র একটি:
calculate(...)
তাই আমরা এর implementation Lambda দিয়ে দিতে পারি।
Calculator addition =
(
first,
second
) -> first + second;
এখন:
addition.calculate(
10,
20
);
return করবে:
30
Why One Abstract Method?
Lambda মূলত একটি behavior-এর implementation দেয়।
যদি interface-এ দুইটি abstract method থাকত:
interface Something {
void first();
void second();
}
তাহলে এই Lambda:
() -> System.out.println(
"Hello"
)
কোন method implement করছে?
first()?
second()?
Ambiguous হয়ে যেত।
একটি abstract method থাকলে mapping পরিষ্কার:
Lambda
→ সেই single abstract method
@FunctionalInterface
Java-তে আমরা functional interface-এর উপর লিখতে পারি:
@FunctionalInterface
Example:
@FunctionalInterface
interface CourseRule {
boolean test(
Course course
);
}
এই annotation compiler-কে জানায়:
এই interface-টি functional interface হিসেবেই design করা হয়েছে।
Is @FunctionalInterface Required?
না।
এই interface-টিও Lambda target হতে পারে:
interface CourseRule {
boolean test(
Course course
);
}
কারণ abstract method মাত্র একটি।
কিন্তু annotation ব্যবহার করা ভালো কারণ future-এ কেউ accidentally আরেকটি abstract method যোগ করলে compiler error দেবে।
Example
Valid:
@FunctionalInterface
interface CourseRule {
boolean test(
Course course
);
}
Invalid:
@FunctionalInterface
interface CourseRule {
boolean test(
Course course
);
boolean anotherRule(
Course course
);
}
Compiler বুঝবে:
এটি আর functional interface নয়।
Default Methods Do Not Break Functional Interface
একটি functional interface-এর একটি abstract method থাকতে হবে।
কিন্তু এতে default method থাকতে পারে।
Example:
@FunctionalInterface
interface CourseRule {
boolean test(
Course course
);
default boolean not(
Course course
) {
return !test(
course
);
}
}
এটি এখনো functional interface।
কারণ abstract method একটি:
test(...)
Static Methods Are Also Fine
@FunctionalInterface
interface CourseRule {
boolean test(
Course course
);
static CourseRule alwaysTrue() {
return course -> true;
}
}
এটিও valid।
static method abstract contract-এর অংশ নয়।
Standard Functional Interfaces
Java ইতিমধ্যে common behavior-এর জন্য reusable functional interfaces দিয়েছে।
এগুলো থাকে:
java.util.function
package-এ।
সবচেয়ে গুরুত্বপূর্ণগুলো:
Predicate<T>
Function<T, R>
Consumer<T>
Supplier<T>
UnaryOperator<T>
BinaryOperator<T>
প্রতিটির একটি specific purpose আছে।
Predicate<T>
Predicate<T> এমন behavior represent করে যা:
একটি value নেয়
এবং
boolean return করে
Mental model:
T
→ boolean
Abstract method:
boolean test(
T value
);
Predicate Example
Predicate<Integer> adultAge =
age -> age >= 18;
Use:
boolean result =
adultAge.test(
20
);
Result:
true
Course Predicate
record Course(
String title,
long priceInPaisa
) {
}
Predicate:
Predicate<Course> paidCourse =
course ->
course.priceInPaisa()
> 0;
Check:
paidCourse.test(
course
);
Read Predicate Naturally
এই code:
course ->
course.priceInPaisa()
> 0
এভাবে পড়তে পারেন:
একটি Course দাও।
Course-এর price zero-এর বেশি হলে true return করো।
Where Predicate Is Commonly Used
Predicate<T> আমরা অনেক জায়গায় দেখি:
removeIf(...)
filter(...)
Example:
courses.removeIf(
course ->
course.priceInPaisa()
== 0
);
removeIf() একটি Predicate নেয়।
Predicate Composition
Suppose আমাদের দুইটি rule আছে।
Predicate<Course> paid =
course ->
course.priceInPaisa()
> 0;
আর:
Predicate<Course> expensive =
course ->
course.priceInPaisa()
>= 500_000;
এগুলো combine করা যায়।
and()
Predicate<Course> paidAndExpensive =
paid.and(
expensive
);
Now:
paidAndExpensive.test(
course
);
true হবে only when:
paid == true
AND
expensive == true
or()
Predicate<Course> paidOrExpensive =
paid.or(
expensive
);
true হবে যখন যেকোনো একটি condition true।
negate()
Predicate<Course> free =
paid.negate();
Meaning:
not paid
Example
Predicate<Course> published =
course ->
course.status()
== CourseStatus.PUBLISHED;
Then:
Predicate<Course> unpublished =
published.negate();
এই composition business rules readable করতে পারে।
Do Not Over-Compose
এমন code:
ruleA
.and(
ruleB
)
.or(
ruleC.negate()
)
.and(
ruleD
)
technically valid হলেও business rule বোঝা কঠিন হতে পারে।
যদি rule meaningful হয়, name দিন:
Predicate<Course> eligibleForPromotion =
paid
.and(
published
);
Readability priority।
Function<T, R>
Function<T, R> একটি input নেয় এবং অন্য একটি result return করে।
Mental model:
T
→ R
Abstract method:
R apply(
T value
);
Function Example
Function<String, Integer> length =
text -> text.length();
Input:
String
Output:
Integer
Use:
int result =
length.apply(
"Java"
);
Result:
4
Course to Title
Function<Course, String> courseTitle =
course ->
course.title();
Use:
String title =
courseTitle.apply(
course
);
Course to Price
Function<Course, Long> coursePrice =
course ->
course.priceInPaisa();
Notice:
Course
→ Long
Transforming Data
Function-এর সবচেয়ে গুরুত্বপূর্ণ mental model:
Transform one value into another.
Examples:
Course → String
String → Integer
Learner → EmailAddress
Enrollment → EnrollmentSummary
Function.andThen()
Suppose:
Function<String, String> trim =
value -> value.strip();
And:
Function<String, String> upper =
value -> value.toUpperCase();
Combine:
Function<String, String> normalize =
trim.andThen(
upper
);
Input:
" java "
Process:
trim first
→ "java"
upper second
→ "JAVA"
andThen() Order
first.andThen(
second
)
means:
first
then
second
compose()
compose() order উল্টো।
Function<String, String> normalize =
upper.compose(
trim
);
Means:
trim first
then upper
because:
upper compose trim
means upper-এর আগে trim apply হবে।
andThen() vs compose()
যদি:
A.andThen(
B
)
then:
A → B
If:
A.compose(
B
)
then:
B → A
Prefer Readable Composition
অনেক chained transformations readable হতে পারে:
Function<String, String> normalize =
trim
.andThen(
upper
);
কিন্তু excessively abstract composition এড়িয়ে চলুন।
একটি named method অনেক সময় clearer:
normalizeCourseCode(...)
Consumer<T>
Consumer<T> একটি value নেয় কিন্তু meaningful return value দেয় না।
Mental model:
T
→ void
Abstract method:
void accept(
T value
);
Consumer Example
Consumer<String> printer =
value ->
System.out.println(
value
);
Use:
printer.accept(
"Java"
);
Output:
Java
Course Consumer
Consumer<Course> printCourse =
course ->
System.out.println(
course.title()
);
Where Consumer Is Common
forEach() Consumer-style behavior নেয়।
Example:
courses.forEach(
course ->
System.out.println(
course.title()
)
);
Consumer.andThen()
Consumers combine করা যায়।
Consumer<String> print =
value ->
System.out.println(
value
);
Another:
Consumer<String> printLength =
value ->
System.out.println(
value.length()
);
Combine:
Consumer<String> both =
print.andThen(
printLength
);
Then:
both.accept(
"Java"
);
runs both behaviors।
Be Careful with Side Effects
Consumer naturally represents side-effecting behavior।
Examples:
Print
Store
Send
Log
Update
এগুলো useful, কিন্তু অনেক side effect chain করলে flow বোঝা কঠিন হতে পারে।
Supplier<T>
Supplier<T> কোনো input নেয় না কিন্তু একটি value return করে।
Mental model:
nothing
→ T
Abstract method:
T get();
Supplier Example
Supplier<String> greeting =
() -> "Hello";
Use:
String value =
greeting.get();
Generate an Object
Supplier<List<String>> listFactory =
() -> new ArrayList<>();
Use:
List<String> values =
listFactory.get();
Supplier and Lazy Creation
Supplier useful যখন value এখনই create করতে চাই না।
Example:
Supplier<String> expensiveMessage =
() -> buildExpensiveMessage();
এখন buildExpensiveMessage() run হবে যখন:
expensiveMessage.get()
call করা হবে।
Supplier in Error Creation
Modern Java APIs-এ এমন pattern frequently দেখা যায়:
orElseThrow(
() ->
new IllegalStateException(
"Course not found."
)
);
এখানে exception creation behavior একটি Supplier-এর মতো।
Optional lesson-এ আমরা এটি বিস্তারিত দেখব।
UnaryOperator<T>
UnaryOperator<T> হচ্ছে special ধরনের Function যেখানে input এবং output একই type।
Mental model:
T
→ T
Example:
UnaryOperator<Integer> doubleValue =
number -> number * 2;
Input:
Integer
Output:
Integer
String Normalization
UnaryOperator<String> normalize =
value ->
value.strip()
.toUpperCase();
Input:
String
Output:
String
Why Not Just Function?
এটিও possible:
Function<String, String>
কিন্তু:
UnaryOperator<String>
আরও specificভাবে communicate করে:
same type in
same type out
BinaryOperator<T>
BinaryOperator<T> দুইটি same-type input নেয় এবং same type result দেয়।
Mental model:
(T, T)
→ T
Example:
BinaryOperator<Integer> addition =
(
first,
second
) -> first + second;
Maximum
BinaryOperator<Integer> maximum =
(
first,
second
) -> Math.max(
first,
second
);
String Combination
BinaryOperator<String> combine =
(
first,
second
) ->
first
+ ", "
+ second;
Functional Interface Cheat Sheet
Predicate<T>
T → boolean
Function<T, R>
T → R
Consumer<T>
T → void
Supplier<T>
() → T
UnaryOperator<T>
T → T
BinaryOperator<T>
(T, T) → T
এগুলো memorization-এর চেয়ে mental shape দিয়ে মনে রাখুন।
Custom Functional Interface
Standard interfaces সব situation cover করবে না।
Suppose:
@FunctionalInterface
interface PriceCalculator {
long calculate(
Course course,
long discountInPaisa
);
}
Then:
PriceCalculator calculator =
(
course,
discount
) ->
course.priceInPaisa()
- discount;
When to Create a Custom Functional Interface
Create one when:
Behavior has meaningful domain semantics
For example:
EnrollmentEligibility
PricePolicy
CourseValidator
But avoid unnecessary new interfaces when standard types already communicate intent well।
Example
Instead of:
@FunctionalInterface
interface CourseCondition {
boolean check(
Course course
);
}
consider:
Predicate<Course>
because it already means:
Course → boolean
But Domain Naming Can Be Valuable
Suppose business concept itself গুরুত্বপূর্ণ:
@FunctionalInterface
interface EnrollmentPolicy {
boolean canEnroll(
Learner learner,
Course course
);
}
This can communicate domain intent much better than a generic:
BiPredicate<Learner, Course>
Generic interface convenience এবং domain meaning-এর মধ্যে balance করতে হবে।
Other Standard Functional Interfaces
Java-তে আরও variants আছে।
Examples:
BiPredicate<T, U>
BiFunction<T, U, R>
BiConsumer<T, U>
BiPredicate<T, U>
Two inputs, boolean output:
(T, U)
→ boolean
Example:
BiPredicate<Integer, Integer> greaterThan =
(
first,
second
) -> first > second;
BiFunction<T, U, R>
Two inputs, one output:
(T, U)
→ R
Example:
BiFunction<String, String, String> fullName =
(
first,
last
) ->
first
+ " "
+ last;
Primitive Specializations
Generic types such as:
Function<Integer, Integer>
may involve boxing/unboxing।
Java provides primitive specializations such as:
IntPredicate
IntFunction<R>
IntConsumer
IntSupplier
IntUnaryOperator
IntBinaryOperator
Example:
IntPredicate positive =
value -> value > 0;
Do Beginners Need All of Them?
না।
Start with:
Predicate
Function
Consumer
Supplier
UnaryOperator
BinaryOperator
Then recognize primitive versions যখন performance বা primitive APIs-এর context-এ আসবে।
Method References
এখন Lambda syntax-এর আরেকটি concise form দেখব:
Method Reference
Suppose:
names.forEach(
name ->
System.out.println(
name
)
);
Lambda শুধু existing method call করছে:
System.out.println(...)
এটাকে shorterভাবে লেখা যায়:
names.forEach(
System.out::println
);
What Does :: Mean?
Method reference syntax uses:
::
It means conceptually:
Use this existing method
as the required behavior.
Lambda vs Method Reference
Lambda:
name ->
System.out.println(
name
)
Method reference:
System.out::println
দুটোর behavior compatible হলে method reference ব্যবহার করা যায়।
Method Reference Is Not Calling the Method Immediately
This:
System.out::println
method call করছে না।
এটি একটি reference to behavior।
Calling would be:
System.out.println(
value
);
Method reference:
behavior itself
Main Method Reference Forms
Common forms:
ClassName::staticMethod
object::instanceMethod
ClassName::instanceMethod
ClassName::new
প্রতিটি আলাদা pattern represent করে।
Static Method Reference
Suppose:
static int parse(
String value
) {
return Integer.parseInt(
value
);
}
Lambda:
Function<String, Integer> parser =
value ->
Integer.parseInt(
value
);
Method reference:
Function<String, Integer> parser =
Integer::parseInt;
Another Static Example
Lambda:
BinaryOperator<Integer> maximum =
(
first,
second
) -> Math.max(
first,
second
);
Method reference:
BinaryOperator<Integer> maximum =
Math::max;
Bound Instance Method Reference
Suppose আমাদের already একটি object আছে:
PrintStream output =
System.out;
Then:
Consumer<String> printer =
output::println;
The object:
output
আগেই fixed।
এটি called object-এর bound instance method reference।
Another Bound Example
String prefix =
"JAVA";
Suppose compatible interface context exists:
Predicate<String> startsWithJava =
prefix::startsWith;
Conceptually:
value ->
prefix.startsWith(
value
)
ClassName::instanceMethod
এটি একটু বেশি confusing হতে পারে।
Example:
Function<String, String> upper =
String::toUpperCase;
Conceptually:
value ->
value.toUpperCase()
এখানে input object-ই method receiver হয়ে যায়।
String Length
Lambda:
Function<String, Integer> length =
value ->
value.length();
Method reference:
Function<String, Integer> length =
String::length;
Comparator Example
Comparator.comparing(
Course::title
);
এখানে:
Course::title
means conceptually:
course ->
course.title()
এটি একটি instance method reference।
Constructor Reference
Suppose:
Supplier<ArrayList<String>> factory =
() ->
new ArrayList<>();
Constructor reference:
Supplier<ArrayList<String>> factory =
ArrayList::new;
Constructor with Parameter
Suppose:
Function<String, CourseCode> factory =
value ->
new CourseCode(
value
);
Can become:
Function<String, CourseCode> factory =
CourseCode::new;
if constructor signature matches।
Method Reference Depends on Target Type
Just like Lambda, method reference needs target context।
This alone:
String::length
does not fully tell Java how you intend to use it।
But:
Function<String, Integer> length =
String::length;
provides the necessary context।
When Method Reference Is Clearer
Good:
names.forEach(
System.out::println
);
Compared with:
names.forEach(
name ->
System.out.println(
name
)
);
Method reference removes unnecessary syntax।
Another Good Example
Lambda:
courses.sort(
Comparator.comparing(
course ->
course.title()
)
);
Better:
courses.sort(
Comparator.comparing(
Course::title
)
);
When Lambda Is Clearer
Do not force method references।
Suppose:
course ->
course.priceInPaisa()
> 500_000
There may not be an existing method that expresses:
is expensive
A Lambda is clearer।
Bad Forced Method Reference Thinking
If you start creating strange helper methods only to write:
CourseRules::isPriceGreaterThanFiveHundredThousand
instead of a simple:
course ->
course.priceInPaisa()
> 500_000
you may be making the code worse।
Use method references when an existing named behavior already fits naturally।
Named Business Rule
On the other hand, if the concept matters:
static boolean isEligibleForPromotion(
Course course
) {
...
}
then:
courses.removeIf(
CourseRules::isNotEligibleForPromotion
);
may be clearer than duplicating a complex Lambda।
Lambda vs Method Reference Rule
A useful rule:
If the Lambda only forwards its parameters
to one existing method,
consider a method reference.
Example:
value ->
Integer.parseInt(
value
)
becomes:
Integer::parseInt
But Readability Wins
Compare:
something::doSomething
with a Lambda where argument mapping is clearer।
If method reference makes the reader stop and mentally decode parameter placement, Lambda may be better।
Practical Example — Predicate
import java.util.function.Predicate;
public class Main {
public static void main(String[] args) {
Predicate<Integer> positive =
value -> value > 0;
Predicate<Integer> even =
value ->
value % 2
== 0;
Predicate<Integer> positiveEven =
positive.and(
even
);
System.out.println(
positiveEven.test(
10
)
);
System.out.println(
positiveEven.test(
-10
)
);
}
}
Output:
true
false
Practical Example — Function
import java.util.function.Function;
public class Main {
public static void main(String[] args) {
Function<String, String> normalize =
value ->
value.strip()
.toUpperCase();
System.out.println(
normalize.apply(
" java "
)
);
}
}
Output:
JAVA
Practical Example — Supplier
import java.util.ArrayList;
import java.util.List;
import java.util.function.Supplier;
public class Main {
public static void main(String[] args) {
Supplier<List<String>> factory =
ArrayList::new;
List<String> courses =
factory.get();
courses.add(
"Java"
);
System.out.println(
courses
);
}
}
Practical Example — Consumer
import java.util.List;
import java.util.function.Consumer;
public class Main {
public static void main(String[] args) {
Consumer<String> printer =
System.out::println;
List<String> names =
List.of(
"Sakib",
"Subu",
"Sumu"
);
names.forEach(
printer
);
}
}
Practical Example — Course Rules
import java.util.function.Predicate;
public class Main {
public static void main(String[] args) {
Course java =
new Course(
"Java Foundation",
300_000,
true
);
Predicate<Course> paid =
course ->
course.priceInPaisa()
> 0;
Predicate<Course> published =
Course::published;
Predicate<Course> purchasable =
paid.and(
published
);
System.out.println(
purchasable.test(
java
)
);
}
record Course(
String title,
long priceInPaisa,
boolean published
) {
}
}
Course::published
Record accessor:
published()
returns:
boolean
So:
Course::published
matches:
Predicate<Course>
because conceptually:
Course
→ boolean
Standard Functional Interface Selection
Suppose আপনি behavior দেখছেন:
Course → boolean
Choose:
Predicate<Course>
If:
Course → String
choose:
Function<Course, String>
If:
Course → void
choose:
Consumer<Course>
If:
() → Course
choose:
Supplier<Course>
Practice 1 — Identify Interface
Behavior:
String → boolean
Which interface?
Answer
Predicate<String>
Practice 2
Behavior:
Course → String
Answer
Function<Course, String>
Practice 3
Behavior:
Learner → void
Answer
Consumer<Learner>
Practice 4
Behavior:
() → Course
Answer
Supplier<Course>
Practice 5
Behavior:
String → String
and input/output একই type।
Answer
UnaryOperator<String>
Function<String, String>-ও কাজ করবে, কিন্তু UnaryOperator বেশি specific।
Practice 6
Behavior:
(Integer, Integer) → Integer
same input/output type।
Answer
BinaryOperator<Integer>
Practice 7 — Predicate Composition
Given:
Predicate<Integer> positive =
value -> value > 0;
Predicate<Integer> even =
value -> value % 2 == 0;
Create a rule for positive and even।
Solution
Predicate<Integer> positiveEven =
positive.and(
even
);
Practice 8 — Negation
Create rule:
not positive
Solution
Predicate<Integer> notPositive =
positive.negate();
Practice 9 — Method Reference
Convert:
value ->
System.out.println(
value
)
Solution
System.out::println
Practice 10
Convert:
value ->
value.length()
with String input।
Solution
String::length
Practice 11
Convert:
value ->
Integer.parseInt(
value
)
Solution
Integer::parseInt
Practice 12
Convert:
() ->
new ArrayList<>()
Solution
ArrayList::new
Practice 13 — Choose Lambda or Method Reference
Which is clearer?
course ->
course.priceInPaisa()
> 500_000
or forcing an unrelated method reference?
Answer
The Lambda।
একটি existing meaningful method না থাকলে simple Lambda clearer।
Practice 14 — Functional Interface Rule
Can this be a Lambda target?
interface Rule {
boolean test(
String value
);
default String description() {
return "Rule";
}
}
Answer
Yes।
Abstract method একটি মাত্র:
test(...)
default method functional-interface status নষ্ট করে না।
Practice 15
Can this be a Lambda target?
interface Rule {
boolean first(
String value
);
boolean second(
String value
);
}
Answer
No।
এখানে দুইটি abstract method আছে।
True or False
- Functional Interface-এর একটি abstract method থাকে।
@FunctionalInterfaceannotation বাধ্যতামূলক।- Functional Interface-এ
defaultmethod থাকতে পারে। Predicate<T>সাধারণতT → booleanbehavior represent করে।Function<T, R>value transformation-এর জন্য useful।Consumer<T>meaningful result return করে।Supplier<T>কোনো input ছাড়াই value দিতে পারে।UnaryOperator<T>-এ input এবং output একই type।BinaryOperator<T>দুইটি same-type input থেকে same-type output দেয়।- Predicate composition-এর জন্য
and(),or(),negate()আছে। - Method Reference
::syntax ব্যবহার করে। - Method Reference method-কে immediately execute করে।
System.out::printlnএকটি bound instance method reference।String::lengthconceptuallyvalue -> value.length()represent করতে পারে।- Method Reference সবসময় Lambda-এর চেয়ে better।
Answers
1. True
2. False
3. True
4. True
5. True
6. False
7. True
8. True
9. True
10. True
11. True
12. False
13. True
14. True
15. False
Knowledge Check
Question 1
Functional Interface কী?
Question 2
@FunctionalInterface কেন useful?
Question 3
Predicate<T> কী ধরনের behavior represent করে?
Question 4
Function<T, R> এবং Consumer<T>-এর মূল difference কী?
Question 5
Supplier<T> কখন useful?
Question 6
UnaryOperator<T> এবং Function<T, T>-এর মধ্যে conceptual difference কী?
Question 7
BinaryOperator<T> কী represent করে?
Question 8
Predicate.and() কী করে?
Question 9
Function.andThen() কী করে?
Question 10
Method Reference কী?
Question 11
ClassName::staticMethod এবং object::instanceMethod-এর difference কী?
Question 12
Method Reference-এর বদলে Lambda কখন clearer হতে পারে?
Knowledge Check Answers
Answer 1
Functional Interface হলো এমন interface যার একটি মাত্র abstract method থাকে এবং যার behavior Lambda Expression দিয়ে implement করা যায়।
Answer 2
এটি compiler-কে জানায় যে interface-টি functional interface হিসেবেই intended। Accidentally দ্বিতীয় abstract method যোগ হলে compiler error দেয়।
Answer 3
এটি একটি value নেয় এবং boolean result দেয়:
T → boolean
Answer 4
Function<T, R> input transform করে একটি result return করে।
T → R
Consumer<T> input নেয় কিন্তু meaningful return value দেয় না।
T → void
Answer 5
যখন কোনো input ছাড়াই value create বা provide করতে চাই।
Example:
Object factory
Lazy value
Exception creation
Answer 6
দুইটিই technically same-type transformation represent করতে পারে।
কিন্তু UnaryOperator<T> বেশি specificভাবে বলে:
T → T
Answer 7
দুইটি same-type value নিয়ে same-type result দেয়:
(T, T) → T
Answer 8
দুইটি Predicate combine করে এমন Predicate তৈরি করে যা তখনই true যখন দুটিই true।
Answer 9
প্রথম Function-এর result দ্বিতীয় Function-এর input হিসেবে দেয়।
Conceptually:
A → B
Answer 10
Method Reference হলো existing method বা constructor-কে functional behavior হিসেবে refer করার concise syntax।
Example:
System.out::println
Answer 11
ClassName::staticMethod
একটি static method refer করে।
object::instanceMethod
আগে থেকেই থাকা একটি specific object-এর instance method refer করে।
Answer 12
যখন Lambda additional logic করে, arguments rearrange করে, condition apply করে, অথবা method reference পড়তে বেশি confusing হয়।
Practical Selection Guide
যদি behavior হয়:
T → boolean
use:
Predicate<T>
যদি:
T → R
use:
Function<T, R>
যদি:
T → void
use:
Consumer<T>
যদি:
() → T
use:
Supplier<T>
যদি:
T → T
use:
UnaryOperator<T>
যদি:
(T, T) → T
use:
BinaryOperator<T>
Method Reference Cheat Sheet
Static method:
Integer::parseInt
Conceptually:
value ->
Integer.parseInt(
value
)
Instance method on existing object:
System.out::println
Conceptually:
value ->
System.out.println(
value
)
Instance method on incoming object:
String::length
Conceptually:
value ->
value.length()
Record accessor:
Course::title
Conceptually:
course ->
course.title()
Constructor:
ArrayList::new
Conceptually:
() ->
new ArrayList<>()
Core Mental Model
Lambda Expression এবং Functional Interface-এর relationship মনে রাখার সবচেয়ে সহজ উপায়:
Functional Interface
→ behavior-এর shape define করে
Lambda
→ সেই behavior-এর implementation দেয়
Example:
Predicate<Course>
defines:
Course → boolean
Lambda:
course ->
course.priceInPaisa()
> 0
gives the actual rule।
আর যদি Lambda শুধু existing method call করে:
course ->
course.title()
তখন আমরা অনেক সময় লিখতে পারি:
Course::title
Lesson Summary
এই lesson-এ আমরা Modern Java-এর functional foundation-এর গুরুত্বপূর্ণ অংশ শিখেছি।
আমরা শিখেছি:
Functional Interface-এ একটি abstract method থাকে- Lambda সেই abstract behavior-এর implementation দেয়
@FunctionalInterfaceintent enforce করতে সাহায্য করেdefaultএবংstaticmethods functional interface-এ থাকতে পারেPredicate<T>condition represent করেFunction<T, R>transformation represent করেConsumer<T>side-effecting consumption represent করেSupplier<T>input ছাড়া value provide করেUnaryOperator<T>same-type transformation represent করেBinaryOperator<T>দুইটি same-type input combine করে- Predicate composition-এর জন্য
and(),or(),negate()আছে - Function composition-এর জন্য
andThen()এবংcompose()আছে - Standard functional interfaces unnecessary custom interfaces কমাতে পারে
- Domain-specific behavior-এর জন্য custom functional interface meaningful হতে পারে
- Method Reference existing behavior-কে conciseভাবে refer করে
::Method Reference syntax- Static method, instance method এবং constructor reference আলাদা pattern
- Method Reference Lambda-এর replacement নয়
- Lambda clearer হলে Lambda ব্যবহার করা উচিত
- Method Reference clearer হলে unnecessary forwarding Lambda বাদ দেওয়া যায়
সবচেয়ে গুরুত্বপূর্ণ mapping:
Predicate
→ Is this true?
Function
→ What does this become?
Consumer
→ What should I do with this?
Supplier
→ Give me a value.
UnaryOperator
→ Transform this into the same type.
BinaryOperator
→ Combine these two values.
Next Lesson
পরবর্তী lesson:
Introduction to the Stream API
আমরা শিখব:
Streamকী- Collection এবং Stream-এর difference
- Stream pipeline
- Source
- Intermediate operation
- Terminal operation
stream()filter()map()forEach()toList()- Lazy evaluation
- Stream একবার consume করা যায় কেন
- Stream data store করে না কেন
- Loop বনাম Stream
- কখন Stream code clearer করে
- কখন traditional loop better