Lambda Expressions & Functional Interfaces
Lambda ও Functional Interface — Java 8 থেকে function-ও value
1. Functions as Values
Before Java 8, behavior was wrapped in anonymous inner classes — five lines of ceremony for one line of logic. A lambda expression is a compact way to supply a function where a functional interface is expected. A functional interface is simply any interface with exactly one abstract method (SAM — Single Abstract Method).
import java.util.*;
import java.util.function.*;
class Main {
public static void main(String[] args) {
// Old style — anonymous inner class
Runnable r1 = new Runnable() {
public void run() { System.out.println("old school"); }
};
r1.run();
// New style — lambda
Runnable r2 = () -> System.out.println("lambda!");
r2.run();
}
}
2. Lambda Syntax
Every lambda is (parameters) -> body. One-parameter lambdas may skip parentheses;
single-expression bodies skip braces and the return.
(parameters) -> body। এক parameter হলে বন্ধনী বাদ যায়; এক-expression body হলে braces ও return বাদ যায়।
import java.util.*;
import java.util.function.*;
class Main {
public static void main(String[] args) {
BiFunction<Integer, Integer, Integer> add = (x, y) -> x + y;
System.out.println("add = " + add.apply(3, 4));
Function<String, Integer> len = s -> s.length();
System.out.println("len = " + len.apply("Bangladesh"));
Function<Integer, Integer> clamp = (Integer n) -> {
if (n < 0) return 0;
if (n > 100) return 100;
return n;
};
System.out.println("clamp(-5) = " + clamp.apply(-5));
System.out.println("clamp(150) = " + clamp.apply(150));
}
}
3. The Four Core Functional Interfaces
java.util.function ships dozens of functional interfaces, but four cover most needs:
Predicate, Function, Consumer, Supplier.
java.util.function-এ অনেকগুলো functional interface আছে, কিন্তু চারটিই ৮০% কাজ সামলায়: Predicate, Function, Consumer, Supplier।
| Interface | Signature | Purpose | বাংলায় |
|---|---|---|---|
Predicate<T> | T → boolean | Filtering/testing. | হ্যাঁ/না প্রশ্ন। |
Function<T,R> | T → R | Transform one value to another. | রূপান্তর। |
Consumer<T> | T → void | Side-effect on a value. | কোনো কিছু পরিবর্তন/print। |
Supplier<T> | () → T | Lazy value produce. | চাহিদামতো value তৈরি। |
BiFunction<A,B,R> | (A,B) → R | Two-arg transform. | দুই-arg transform। |
UnaryOperator<T> | T → T | Same-type transform. | Same type-এ transform। |
import java.util.*;
import java.util.function.*;
class Main {
public static void main(String[] args) {
Predicate<String> isLong = s -> s.length() > 5;
Function<String, String> shout = s -> s.toUpperCase();
Consumer<String> printer = System.out::println;
Supplier<String> greet = () -> "Hello from lambda!";
System.out.println("isLong('Arif') = " + isLong.test("Arif"));
System.out.println("isLong('Bangladesh') = " + isLong.test("Bangladesh"));
System.out.println("shout = " + shout.apply("java"));
printer.accept(greet.get());
}
}
4. Variable Capture — Effectively Final
A lambda can read local variables from its enclosing scope, but those variables must be effectively final — that is, assigned once and never reassigned. This rule prevents subtle bugs when the lambda runs later on another thread.
import java.util.*;
import java.util.function.*;
class Main {
public static void main(String[] args) {
int threshold = 10; // effectively final
Predicate<Integer> big = n -> n > threshold;
System.out.println(big.test(5)); // false
System.out.println(big.test(42)); // true
// threshold = 20; // would break: lambda no longer allowed to capture
// Capturing a field of an object works fine
int[] counter = { 0 }; // array is effectively final — contents can change
Runnable tick = () -> counter[0]++;
tick.run(); tick.run(); tick.run();
System.out.println("counter = " + counter[0]);
}
}
5. Defining Your Own Functional Interface
Mark any single-abstract-method interface with @FunctionalInterface. The annotation is
optional but catches accidental second-method additions at compile time.
@FunctionalInterface দিয়ে mark করুন — এতে ভুল করে দ্বিতীয় abstract method যোগ করলে compile-time-এ ধরা পড়বে।
class Main {
@FunctionalInterface
interface DistanceCalc {
double between(double a, double b);
}
public static void main(String[] args) {
DistanceCalc manhattan = (a, b) -> Math.abs(a - b);
DistanceCalc squared = (a, b) -> (a - b) * (a - b);
System.out.println("manhattan = " + manhattan.between(3, 10));
System.out.println("squared = " + squared.between(3, 10));
}
}
6. Vocabulary
| Term | Meaning | বাংলায় |
|---|---|---|
| SAM | Single Abstract Method — the rule for a functional interface. | ঠিক একটি abstract method — functional interface-এর শর্ত। |
| Lambda | Compact syntax: (params) -> body. | সংক্ষিপ্ত syntax (params) -> body। |
| Effectively final | Assigned once, never reassigned. | একবার assign, আর কখনো নয়। |
@FunctionalInterface | Marker annotation enforcing SAM. | SAM check করার annotation। |
java.util.function | Standard library of common functional interfaces. | Standard functional interface-এর package। |
7. Practice Problems
-
Write a
Predicate<String>that returns true for strings starting with "A"."A" দিয়ে শুরু হওয়া string-এর জন্য true দেয় এমনPredicate<String>লিখুন।✨ Show Answer
Main.javaimport java.util.function.*; class Main { public static void main(String[] args) { Predicate<String> startsA = s -> s.startsWith("A"); System.out.println(startsA.test("Arif")); System.out.println(startsA.test("Rina")); } } -
Implement a
Function<Integer, Integer>that doubles its argument; test with 7.Function<Integer, Integer>লিখুন যা argument দ্বিগুণ করে; 7 দিয়ে পরীক্ষা করুন।✨ Show Answer
Main.javaimport java.util.function.*; class Main { public static void main(String[] args) { Function<Integer, Integer> dbl = n -> n * 2; System.out.println(dbl.apply(7)); } } -
Sort a list of strings by length using a
Comparatorlambda.string-এর list-কে length অনুযায়ীComparatorlambda দিয়ে sort করুন।✨ Show Answer
Main.javaimport java.util.*; class Main { public static void main(String[] args) { List<String> xs = new ArrayList<>(List.of("hi", "hello", "yo", "welcome")); xs.sort((a, b) -> Integer.compare(a.length(), b.length())); System.out.println(xs); } } -
Explain "effectively final" in 2–3 sentences."effectively final" মানে 2–3 বাক্যে ব্যাখ্যা করুন।
✨ Show Answer
Answer: A local variable is "effectively final" if, even without the
finalkeyword, it is assigned exactly once and never reassigned. Lambdas and inner classes can only capture such variables because the value must remain stable if the lambda runs later on another thread. Addfinalexplicitly if you want the compiler to enforce it.একটি local variable "effectively final" যদি
finalkeyword না থাকলেও সেটি শুধু একবার assign হয় এবং আর কখনো পরিবর্তিত হয় না। Lambda বা inner class এই ধরনের variable-ই capture করতে পারে — কারণ লাইফটাইম/thread bound-এর কারণে value স্থির থাকা দরকার। চাইলেfinalkeyword লিখে compiler-কে enforce করতে বলুন। -
Use a
BiFunction<Integer, Integer, Integer>to compute GCD of two numbers.BiFunctionদিয়ে দুটি সংখ্যার GCD বের করুন।✨ Show Answer
Main.javaimport java.util.function.*; class Main { public static void main(String[] args) { BiFunction<Integer, Integer, Integer> gcd = (a, b) -> { while (b != 0) { int t = b; b = a % b; a = t; } return a; }; System.out.println(gcd.apply(48, 18)); System.out.println(gcd.apply(100, 75)); } }
Summary — Module 27
A lambda is a compact way to supply a single-abstract-method (SAM) function. Java 8's
java.util.function gives you Predicate, Function,
Consumer, Supplier, and their arity/type variants. Captured local variables must be
effectively final. Use @FunctionalInterface on your own SAM interfaces for compile-time safety.
java.util.function-এ Predicate/Function/Consumer/Supplier ইত্যাদি রেডি। Captured variable effectively final হতে হবে। নিজের SAM interface-এ @FunctionalInterface দিন।