Java 8 এ Functional Programming ধারণা আনা হয়েছে এবং এর অংশ হিসেবে Functional Interfaces পরিচিতি পেয়েছে। Functional Interface এমন একটি ইন্টারফেস যা শুধুমাত্র একটি abstract method ধারণ করে এবং lambda expressions বা method references দ্বারা এই ইন্টারফেসের মেথড বাস্তবায়ন করা যায়। Java 8 থেকে অনেক Built-in Functional Interfaces অন্তর্ভুক্ত করা হয়েছে, যা ফাংশনাল প্রোগ্রামিংয়ের কাজকে আরও সহজ এবং শক্তিশালী করে তোলে।
এখানে কিছু জনপ্রিয় Built-in Functional Interfaces এবং তাদের ব্যবহার তুলে ধরা হলো:
1. Predicate<T>
Predicate<T> একটি Functional Interface যা একটি boolean মান রিটার্ন করে এবং একটি input নেয়। এটি সাধারণত condition testing এর জন্য ব্যবহৃত হয়, যেখানে আপনি একটি কন্ডিশন যাচাই করেন এবং true/false রিটার্ন করেন।
Methods:
boolean test(T t)— যা একটি অবজেক্ট পরীক্ষা করে এবং একটি boolean রিটার্ন করে।
উদাহরণ:
import java.util.function.Predicate;
public class PredicateExample {
public static void main(String[] args) {
// Predicate to check if a number is even
Predicate<Integer> isEven = number -> number % 2 == 0;
// Test the predicate
System.out.println(isEven.test(10)); // Output: true
System.out.println(isEven.test(7)); // Output: false
}
}
এখানে, isEven Predicate টি একটি সংখ্যা যাচাই করে, যদি তা even হয় তবে true রিটার্ন করবে।
2. Function<T, R>
Function<T, R> একটি Functional Interface যা একটি ইনপুট অবজেক্ট নেয় এবং একটি আউটপুট রিটার্ন করে। এটি সাধারণত transformation বা mapping এর জন্য ব্যবহৃত হয়।
Methods:
R apply(T t)— একটি ইনপুট অবজেক্ট নেয় এবং একটি আউটপুট রিটার্ন করে।default <V> Function<T,V> andThen(Function<? super R, ? extends V> after)— ফাংশন চেইনিংয়ের জন্য ব্যবহৃত হয়।
উদাহরণ:
import java.util.function.Function;
public class FunctionExample {
public static void main(String[] args) {
// Function to convert string to uppercase
Function<String, String> toUpperCase = str -> str.toUpperCase();
// Apply the function
System.out.println(toUpperCase.apply("hello")); // Output: HELLO
}
}
এখানে, toUpperCase Function টি একটি স্ট্রিং ইনপুট নেয় এবং সেটিকে uppercase রূপে রিটার্ন করে।
3. Consumer<T>
Consumer<T> একটি Functional Interface যা একটি ইনপুট নেয় এবং কোন কিছু রিটার্ন না করে তার সাথে কাজ করে। এটি সাধারণত side-effecting operations (যেমন প্রিন্টিং, ডাটাবেসে লেখা, বা মিউটেবল ডেটা পরিবর্তন) এর জন্য ব্যবহৃত হয়।
Methods:
void accept(T t)— একটি অবজেক্ট নেয় এবং তার উপরে কিছু কাজ করে (কিন্তু কোন ভ্যালু রিটার্ন করে না)।default Consumer<T> andThen(Consumer<? super T> after)— একটি চেইনড কনসিউমার তৈরি করতে ব্যবহৃত হয়।
উদাহরণ:
import java.util.function.Consumer;
public class ConsumerExample {
public static void main(String[] args) {
// Consumer to print a string
Consumer<String> printString = str -> System.out.println(str);
// Using the Consumer
printString.accept("Hello, World!"); // Output: Hello, World!
}
}
এখানে, printString Consumer টি একটি স্ট্রিং নেয় এবং সেটিকে কনসোলে প্রিন্ট করে।
4. Supplier<T>
Supplier<T> একটি Functional Interface যা কোনো ইনপুট নেয় না, কিন্তু একটি আউটপুট প্রদান করে। এটি সাধারণত lazy evaluation বা value generation এর জন্য ব্যবহৃত হয়।
Methods:
T get()— একটি ভ্যালু রিটার্ন করে, যেটি পরে প্রাপ্ত হবে।
উদাহরণ:
import java.util.function.Supplier;
public class SupplierExample {
public static void main(String[] args) {
// Supplier to generate a random number
Supplier<Double> randomNumberSupplier = () -> Math.random();
// Get the random number
System.out.println(randomNumberSupplier.get());
}
}
এখানে, randomNumberSupplier Supplier টি একটি র্যান্ডম সংখ্যা রিটার্ন করে।
5. UnaryOperator<T>
UnaryOperator<T> হল Function<T, T> এর একটি বিশেষীকৃত সংস্করণ, যা একটি ইনপুট নেয় এবং একই ধরনের আউটপুট রিটার্ন করে। এটি সাধারণত কোনো একটি একক ডেটার উপর অপারেশন পরিচালনা করতে ব্যবহৃত হয়।
Methods:
T apply(T t)— একটি ইনপুট নেয় এবং একটি একই ধরনের আউটপুট রিটার্ন করে।
উদাহরণ:
import java.util.function.UnaryOperator;
public class UnaryOperatorExample {
public static void main(String[] args) {
// UnaryOperator to double the value
UnaryOperator<Integer> doubleValue = num -> num * 2;
// Apply the UnaryOperator
System.out.println(doubleValue.apply(5)); // Output: 10
}
}
এখানে, doubleValue UnaryOperator টি একটি ইন্টিজার ইনপুট নেয় এবং সেটি ডাবল করে রিটার্ন করে।
6. BinaryOperator<T>
BinaryOperator<T> হল BiFunction<T, T, R> এর একটি বিশেষীকৃত সংস্করণ, যা দুটি ইনপুট নেয় এবং একটি আউটপুট রিটার্ন করে, যেখানে ইনপুট এবং আউটপুট একই ধরনের হতে হবে।
Methods:
T apply(T t1, T t2)— দুটি ইনপুট নেয় এবং একটি আউটপুট রিটার্ন করে।
উদাহরণ:
import java.util.function.BinaryOperator;
public class BinaryOperatorExample {
public static void main(String[] args) {
// BinaryOperator to add two numbers
BinaryOperator<Integer> addNumbers = (a, b) -> a + b;
// Apply the BinaryOperator
System.out.println(addNumbers.apply(5, 10)); // Output: 15
}
}
এখানে, addNumbers BinaryOperator টি দুটি ইন্টিজার ইনপুট নেয় এবং তাদের যোগফল রিটার্ন করে।
7. Comparator<T>
Comparator<T> একটি Functional Interface যা দুটি অবজেক্টের তুলনা করার জন্য ব্যবহৃত হয়। এটি সাধারণত sorting এবং ordering এর জন্য ব্যবহৃত হয়।
Methods:
int compare(T o1, T o2)— দুটি অবজেক্টের তুলনা করে এবং একটি ইন্টিজার রিটার্ন করে।
উদাহরণ:
import java.util.Arrays;
import java.util.Comparator;
public class ComparatorExample {
public static void main(String[] args) {
// Sample data
String[] names = {"Alice", "Bob", "Charlie", "David"};
// Using Comparator to sort names alphabetically
Arrays.sort(names, Comparator.naturalOrder());
// Print the sorted names
System.out.println(Arrays.toString(names)); // Output: [Alice, Bob, Charlie, David]
}
}
এখানে, Comparator.naturalOrder() ব্যবহার করা হয়েছে যা অবজেক্টগুলিকে স্বাভাবিক অর্ডারে সাজায়।
8. Optional<T>
Optional<T> হল একটি container object যা null বা non-null value ধারণ করতে পারে। এটি ব্যবহারকারীকে null সেফ কোড লিখতে সহায়তা করে।
Methods:
T get()— যদি মান উপলব্ধ থাকে, তবে সেটি রিটার্ন করে।boolean isPresent()— চেক করে যে মান উপস্থিত আছে কি না।T orElse(T other)— যদি মান উপস্থিত না থাকে, তবে ডিফল্ট মান রিটার্ন করে।
উদাহরণ:
import java.util.Optional;
public class OptionalExample {
public static void main(String[] args) {
Optional<String> optionalValue = Optional.ofNullable("Hello World");
// Using ifPresent() to check if value is present
optionalValue.ifPresent(value -> System.out.println(value)); // Output: Hello World
// Using orElse() to provide a default value
System.out.println(optionalValue.orElse("Default Value")); // Output: Hello World
}
}
সারাংশ:
Java 8 থেকে Functional Interfaces এমন গুরুত্বপূর্ণ বৈশিষ্ট্য প্রদান করেছে যা কোডকে পরিষ্কার, সংক্ষিপ্ত, এবং কার্যকরী করে তোলে। বিভিন্ন Built-in Functional Interfaces যেমন Predicate, Function, Consumer, Supplier, Comparator, UnaryOperator, এবং BinaryOperator আপনাকে কার্যক্ষম, এক্সপ্রেসিভ এবং ফাংশনাল প্রোগ্রামিং এর সুবিধা প্রদান করে।
এগুলি কোডের পুনঃব্যবহারযোগ্যতা বৃদ্ধি করে এবং কোডের readability এবং maintainability আরও উন্নত করে, যা lambda expressions এবং method references এর মাধ্যমে সহজে ব্যবহৃত হতে পারে।
Predicate Interface হল java.util.function প্যাকেজের একটি বিল্ট-ইন ফাংশনাল ইন্টারফেস, যা boolean-valued function রিটার্ন করে। অর্থাৎ, Predicate একটি ফাংশনাল ইন্টারফেস যা একটি ইনপুট গ্রহণ করে এবং তার ভিত্তিতে একটি boolean ফলাফল প্রদান করে (যেমন, একটি শর্ত যাচাই করা)। এটি খুবই উপকারী যখন আপনি কিছু যাচাই করতে চান, যেমন একটি কন্ডিশন চেক করা বা একটি ফিল্টার অপারেশন করা।
Predicate Interface প্রধানত filtering, matching, validation, এবং logical operations এর জন্য ব্যবহৃত হয়।
1. Predicate Interface এর কাঠামো
Predicate<T> ইন্টারফেসে একটি মাত্র abstract method থাকে:
boolean test(T t);
Tহচ্ছে ইনপুট প্যারামিটার টাইপ (যেমন Integer, String, Custom Objects ইত্যাদি)।test(T t)মেথডটি ইনপুট গ্রহণ করে এবং একটিbooleanমান রিটার্ন করে।
2. Predicate Interface এর মৌলিক ব্যবহার
Basic Predicate Example:
import java.util.function.Predicate;
public class PredicateExample {
public static void main(String[] args) {
// A Predicate that checks if a number is greater than 10
Predicate<Integer> isGreaterThanTen = number -> number > 10;
System.out.println(isGreaterThanTen.test(5)); // Output: false
System.out.println(isGreaterThanTen.test(15)); // Output: true
}
}
Explanation:
- এখানে, Predicate isGreaterThanTen একটি ফাংশনাল ইন্টারফেস তৈরি করা হয়েছে যা একটি
Integerইনপুট নিয়ে যাচাই করে, যদি তা 10 এর বেশি হয় তবেtrueরিটার্ন করবে, অন্যথায়false।
3. Predicate Chaining (Combining Predicates)
Java 8 থেকে Predicate এর সাথে logical operations যেমন AND, OR, এবং NEGATE করার সুবিধা রয়েছে। আপনি and(), or(), এবং negate() মেথড ব্যবহার করে একাধিক Predicate একসাথে চেইন করতে পারেন।
Predicate Chaining Example:
import java.util.function.Predicate;
public class PredicateChaining {
public static void main(String[] args) {
Predicate<Integer> isGreaterThanTen = number -> number > 10;
Predicate<Integer> isEven = number -> number % 2 == 0;
// Chaining predicates: Check if a number is greater than 10 AND even
System.out.println(isGreaterThanTen.and(isEven).test(12)); // Output: true
System.out.println(isGreaterThanTen.and(isEven).test(5)); // Output: false
// Chaining predicates with OR: Check if a number is greater than 10 OR even
System.out.println(isGreaterThanTen.or(isEven).test(8)); // Output: true
System.out.println(isGreaterThanTen.or(isEven).test(7)); // Output: false
// Using negate: Check if a number is NOT greater than 10
System.out.println(isGreaterThanTen.negate().test(5)); // Output: true
}
}
Explanation:
- and(): দুটি Predicate একসাথে AND অপারেশন চালায়।
- or(): দুটি Predicate একসাথে OR অপারেশন চালায়।
- negate(): একটি Predicate এর ফলাফল উল্টো করে দেয় (NOT অপারেশন)।
4. Predicate Interface with Collections
Predicate এর ব্যবহার সবচেয়ে বেশি ফিল্টারিং এবং শর্তসাপেক্ষ অপারেশনগুলোতে হয়। আপনি Streams API ব্যবহার করে Predicate এর মাধ্যমে একটি Collection বা List ফিল্টার করতে পারেন।
Predicate with Collections Example:
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collectors;
public class PredicateWithCollections {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(5, 12, 8, 21, 3, 14);
// Predicate to check if a number is even
Predicate<Integer> isEven = number -> number % 2 == 0;
// Using Predicate to filter the list
List<Integer> evenNumbers = numbers.stream()
.filter(isEven)
.collect(Collectors.toList());
System.out.println("Even numbers: " + evenNumbers); // Output: [12, 8, 14]
}
}
Explanation:
- Predicate
isEvenব্যবহার করে আমরা stream এর মধ্যে ফিল্টারিং করেছি, যাতে কেবল even সংখ্যাগুলি collect করা হয়। filter(isEven)ফিল্টার অপারেশন ব্যবহার করে Predicate মেথডের সাথে ইন্টারঅ্যাক্ট করে এবং ফলস্বরূপ শুধুমাত্র even সংখ্যাগুলি List হিসেবে রিটার্ন করে।
5. Practical Use Case for Predicate: Validation
Predicate এর মাধ্যমে ডেটা যাচাই বা validation করা খুবই সহজ। আপনি Predicate ব্যবহার করে কাস্টম ডেটা ভ্যালিডেশন করতে পারেন, যেমন checking eligibility, password validation, ইত্যাদি।
Password Validation Example:
import java.util.function.Predicate;
public class PasswordValidator {
public static void main(String[] args) {
// Predicate to check if password has at least one digit
Predicate<String> hasDigit = password -> password.matches(".*\\d.*");
// Predicate to check if password length is at least 8 characters
Predicate<String> lengthAtLeastEight = password -> password.length() >= 8;
// Combine predicates to validate the password
Predicate<String> isValidPassword = hasDigit.and(lengthAtLeastEight);
System.out.println(isValidPassword.test("Password123")); // Output: true
System.out.println(isValidPassword.test("pass123")); // Output: false
}
}
Explanation:
- এখানে দুটি Predicate তৈরি করা হয়েছে: একটি digit চেক করার জন্য এবং অন্যটি length চেক করার জন্য।
- এরপর and() ব্যবহার করে দুটি Predicate একসাথে যোগ করা হয়েছে যা ভ্যালিড পাসওয়ার্ড যাচাই করে।
6. Predicate for Optional
Predicate কে Optional এর সাথে ব্যবহার করে আপনি নাল ভ্যালু যাচাই করতে পারেন এবং নিরাপদভাবে ডেটা প্রসেস করতে পারেন।
Predicate with Optional Example:
import java.util.Optional;
import java.util.function.Predicate;
public class PredicateWithOptional {
public static void main(String[] args) {
Optional<String> username = Optional.of("JohnDoe");
Predicate<String> isNotEmpty = s -> s != null && !s.isEmpty();
boolean result = username.filter(isNotEmpty).isPresent();
System.out.println("Is username present and not empty? " + result); // Output: true
}
}
Explanation:
- Optional ব্যবহার করে আমরা প্রথমে username ভেরিয়েবলের মান চেক করি। filter ব্যবহার করে Predicate যাচাই করে, তারপর isPresent চেক করি।
সারাংশ
Predicate Interface Java Functional Programming এর একটি গুরুত্বপূর্ণ অংশ, যা boolean ফলাফল রিটার্ন করার জন্য ব্যবহার করা হয়। এটি শর্ত যাচাই, filtering, validation, matching ইত্যাদির জন্য খুবই উপকারী। Predicate এর মাধ্যমে আপনি logical operations (AND, OR, NOT) করতে পারেন, এবং তা Collections বা Streams এর সাথে ব্যবহার করে আরও কার্যকরী অপারেশন করতে পারেন। Java 8 এ Predicate এর সাহায্যে ফাংশনাল প্রোগ্রামিং অনেক সহজ হয়ে গেছে, যা কোডকে আরও ক্লিন, স্কেলেবল এবং পড়তে সহজ করে তোলে।
Function Interface হল Java 8-এ যুক্ত একটি ফাংশনাল ইন্টারফেস যা java.util.function প্যাকেজে সংজ্ঞায়িত হয়েছে। এটি একটি Functional Interface, যার মধ্যে শুধুমাত্র একটি অ্যাবস্ট্রাক্ট মেথড থাকে। ফাংশনাল ইন্টারফেসগুলিকে মূলত Lambda Expressions বা Method References দ্বারা ইমপ্লিমেন্ট করা হয়।
Function Interface হল একটি প্রকারের generic functional interface, যা একটি ইনপুট গ্রহণ করে এবং একটি আউটপুট প্রদান করে। এটি UnaryOperator বা BinaryOperator এর মতো অন্যান্য ফাংশনাল ইন্টারফেসের উপর ভিত্তি করে কাজ করে।
Function Interface এর সঠিক সাইনট্যাক্স:
@FunctionalInterface
public interface Function<T, R> {
R apply(T t); // Single abstract method
}
এখানে:
- T হল ইনপুট টাইপ।
- R হল আউটপুট টাইপ।
- apply() মেথড হল ফাংশনাল ইন্টারফেসের একমাত্র অ্যাবস্ট্রাক্ট মেথড, যা ইনপুট গ্রহণ করে এবং আউটপুট প্রদান করে।
Function Interface এর প্রধান বৈশিষ্ট্য
- Single Abstract Method:
- Function Interface এর মধ্যে একমাত্র অ্যাবস্ট্রাক্ট মেথড থাকে, যেটি apply(T t)। এটি একটি ইনপুট T গ্রহণ করে এবং একটি আউটপুট R প্রদান করে।
- Generic Types:
- Function Interface জেনেরিক টাইপ নিয়ে কাজ করে। এটি সাধারণত দুইটি টাইপ প্যারামিটার নেয়: ইনপুট টাইপ T এবং আউটপুট টাইপ R।
- Higher-order Function:
- আপনি Function Interface এর মাধ্যমে ফাংশনাল প্রোগ্রামিং স্টাইল ব্যবহার করে এক ফাংশনকে অন্য ফাংশনের আর্গুমেন্ট বা রিটার্ন ভ্যালু হিসেবে পাস করতে পারেন। এর মাধ্যমে আপনি আরও ফ্লেক্সিবিলিটি এবং মডুলার কোড তৈরি করতে পারেন।
Function Interface এর ব্যবহার
Function Interface ব্যবহার করে আপনি সহজে Lambda Expressions এবং Method References ব্যবহার করে ইনপুট থেকে আউটপুট জেনারেট করতে পারেন।
1. Basic Example Using Function Interface
import java.util.function.Function;
public class FunctionExample {
public static void main(String[] args) {
// Using Function Interface with Lambda Expression
Function<Integer, Integer> square = x -> x * x;
// Applying the function
System.out.println(square.apply(5)); // Output: 25
}
}
ব্যাখ্যা:
- এখানে Function<Integer, Integer> square একটি Function Interface যা একটি ইনপুট Integer গ্রহণ করে এবং একটি আউটপুট Integer প্রদান করে। আমরা Lambda Expression ব্যবহার করে apply() মেথডটি ইমপ্লিমেন্ট করেছি, যা ইনপুটের স্কয়ার রিটার্ন করবে।
2. Function Interface with Chaining
Function Interface চেইনিংয়ের জন্য অনেক উপকারী, কারণ আপনি একাধিক Function ইন্টারফেসের মধ্যে কার্যক্রম সংযুক্ত করতে পারেন। andThen() এবং compose() মেথডের মাধ্যমে ফাংশন চেইন করা যায়।
- andThen(): এটি একটি ফাংশনের পরে আরেকটি ফাংশন প্রয়োগ করে। প্রথম ফাংশনটির আউটপুট দ্বিতীয় ফাংশনের ইনপুট হিসেবে চলে যায়।
- compose(): এটি একটি ফাংশনের আগে অন্য একটি ফাংশন প্রয়োগ করে। দ্বিতীয় ফাংশনটি প্রথম ফাংশনের ইনপুট হিসেবে চলে যায়।
Example with Chaining Functions:
import java.util.function.Function;
public class FunctionChainingExample {
public static void main(String[] args) {
// Define two functions using Function Interface
Function<Integer, Integer> multiplyBy2 = x -> x * 2;
Function<Integer, Integer> add10 = x -> x + 10;
// Chaining using andThen()
Function<Integer, Integer> multiplyAndThenAdd = multiplyBy2.andThen(add10);
System.out.println(multiplyAndThenAdd.apply(5)); // Output: 20 (5*2 + 10)
// Chaining using compose()
Function<Integer, Integer> addThenMultiply = multiplyBy2.compose(add10);
System.out.println(addThenMultiply.apply(5)); // Output: 30 (5 + 10 * 2)
}
}
ব্যাখ্যা:
multiplyBy2.andThen(add10)প্রথমেmultiplyBy2ফাংশন চালাবে এবং তারপরে তার আউটপুটের উপরadd10ফাংশন প্রয়োগ করবে।multiplyBy2.compose(add10)প্রথমেadd10ফাংশন চালাবে এবং তারপরেmultiplyBy2ফাংশন প্রয়োগ করবে।
Function Interface with Other Functional Interfaces
Java 8 তে অনেক ফাংশনাল ইন্টারফেস রয়েছে যা Function ইন্টারফেসের সাথে মিলে কাজ করতে পারে, যেমন:
- Predicate: এটি একটি Boolean রিটার্ন করে এবং সাধারণত শর্ত যাচাই করতে ব্যবহৃত হয়।
- Consumer: এটি কোনো রিটার্ন ভ্যালু ছাড়া আর্গুমেন্ট গ্রহণ করে।
- Supplier: এটি কোনো ইনপুট ছাড়াই একটি আউটপুট প্রদান করে।
Example with Multiple Functional Interfaces:
import java.util.function.*;
public class FunctionWithOtherInterfaces {
public static void main(String[] args) {
// Function: Takes an Integer, returns its square
Function<Integer, Integer> square = x -> x * x;
// Predicate: Checks if the number is positive
Predicate<Integer> isPositive = x -> x > 0;
// Consumer: Prints the number
Consumer<Integer> print = System.out::println;
// Using Function to calculate square
System.out.println(square.apply(4)); // Output: 16
// Using Predicate to check if number is positive
System.out.println(isPositive.test(4)); // Output: true
System.out.println(isPositive.test(-4)); // Output: false
// Using Consumer to print the number
print.accept(5); // Output: 5
}
}
ব্যাখ্যা:
- এখানে Function একে অপরের সাথে ব্যবহার করা হয়েছে অন্যান্য ফাংশনাল ইন্টারফেসের সাথে, যেমন Predicate (যা Boolean রিটার্ন করে) এবং Consumer (যা কোনো ভ্যালু গ্রহণ করে এবং সেটি প্রিন্ট করে)।
Function Interface এর সুবিধা এবং উপকারিতা:
- Declarative Programming:
- Function Interface ব্যবহার করে আপনি declarative programming স্টাইলে কাজ করতে পারেন। এটি কোডকে পরিষ্কার এবং স্বচ্ছ করে তোলে।
- Code Reusability:
- আপনি যেকোনো ফাংশনকে higher-order function হিসেবে পাস বা রিটার্ন করতে পারেন, যা কোডের পুনঃব্যবহারযোগ্যতা বাড়ায়।
- Chaining:
- andThen() এবং compose() মেথডের মাধ্যমে একাধিক ফাংশন চেইন করা সম্ভব, যা আপনার কাজের মধ্যে আরও ফ্লেক্সিবিলিটি এবং কার্যকারিতা আনে।
- Separation of Concerns:
- বিভিন্ন কাজ বা কার্যাবলী পৃথকভাবে সংজ্ঞায়িত করতে পারলে কোডের মেইনটেন্যান্স এবং রিডেবিলিটি বৃদ্ধি পায়।
সারাংশ:
Function Interface হল Java 8 এর একটি শক্তিশালী ফিচার যা ফাংশনাল প্রোগ্রামিংয়ের ধারণাকে বাস্তবায়িত করে। এটি একটি generic functional interface যা ইনপুট থেকে আউটপুট তৈরির জন্য ব্যবহার হয়। এটি Lambda Expressions এবং Method References এর মাধ্যমে কোডকে আরো সহজ, পরিষ্কার এবং পুনঃব্যবহারযোগ্য করে তোলে। Function Interface ব্যবহারের মাধ্যমে ফাংশনাল প্রোগ্রামিংয়ের সুবিধাগুলি (যেমন declarative style, chaining, higher-order functions) কার্যকরভাবে প্রয়োগ করা যায়।
Java 8-এ functional programming এর ধারণা সমর্থন করতে java.util.function প্যাকেজে বেশ কিছু ইন্টারফেস যোগ করা হয়েছে, যার মধ্যে Consumer এবং Supplier ইন্টারফেস দুটি গুরুত্বপূর্ণ। এই ইন্টারফেস দুটি Java-তে functional-style programming করার জন্য ব্যবহৃত হয়, বিশেষত lambda expressions এবং method references এর সাথে।
এই ইন্টারফেস দুটি সাধারণভাবে functional interfaces হিসেবে কাজ করে, অর্থাৎ এগুলির মধ্যে একটি মাত্র abstract method থাকে, যা ল্যাম্বডা এক্সপ্রেশন বা মেথড রেফারেন্সের মাধ্যমে ইমপ্লিমেন্ট করা যায়।
1. Consumer Interface:
Consumer ইন্টারফেসটি এমন একটি ফাংশনাল ইন্টারফেস যা এক ধরনের ইনপুট নেয় এবং কোনো আউটপুট প্রদান করে না (void return type)। এটি মূলত side-effect তৈরি করতে ব্যবহৃত হয়, যেমন প্রিন্ট করা, ডেটাবেসে ডেটা ইনসার্ট করা, অথবা কোনো অবজেক্ট পরিবর্তন করা।
Consumer Interface এর সিগনেচার:
@FunctionalInterface
public interface Consumer<T> {
void accept(T t);
}
এখানে:
- T: ইনপুট প্যারামিটার টাইপ।
- accept(T t): একটি ইনপুট গ্রহণ করে এবং কিছু কাজ (সাধারণত সাইড-এফেক্ট) সম্পন্ন করে।
Consumer Interface ব্যবহার:
Example 1:
import java.util.function.Consumer;
public class ConsumerExample {
public static void main(String[] args) {
// Lambda expression implementation of Consumer
Consumer<String> printUpperCase = str -> System.out.println(str.toUpperCase());
printUpperCase.accept("hello"); // Output: HELLO
}
}
এখানে:
- Consumer একটি Consumer টাইপের অবজেক্ট যা String ইনপুট নেয় এবং
System.out.println()দ্বারা আউটপুট প্রদান করে (side effect)। accept()মেথডের মাধ্যমে ইনপুট প্যারামিটার গ্রহণ করা হয় এবং কাজ সম্পাদিত হয়।
Example 2:
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;
public class ConsumerExample {
public static void main(String[] args) {
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
// Using Consumer to print each name
Consumer<String> printName = name -> System.out.println(name);
names.forEach(printName);
}
}
এখানে:
forEach()মেথডের মাধ্যমে Consumer ব্যবহার করে লিস্টের প্রতিটি আইটেম প্রিন্ট করা হচ্ছে।
Chaining Consumers:
আপনি একাধিক Consumer একত্রে যোগ করতে পারেন andThen() মেথড ব্যবহার করে।
Consumer<String> printUpper = str -> System.out.println(str.toUpperCase());
Consumer<String> printLower = str -> System.out.println(str.toLowerCase());
printUpper.andThen(printLower).accept("Hello");
Output:
HELLO
hello
এখানে, প্রথমে printUpper কল হবে, তারপর printLower কল হবে।
2. Supplier Interface:
Supplier ইন্টারফেসটি এমন একটি ফাংশনাল ইন্টারফেস যা কোনো ইনপুট না নিয়ে আউটপুট প্রদান করে। এটি সাধারণত lazy evaluation বা factory methods তৈরি করতে ব্যবহৃত হয়, যেখানে একটি মান প্রাপ্তির জন্য কোনো নির্দিষ্ট অবজেক্ট বা কম্পিউটেশন তৈরি করা হয়।
Supplier Interface এর সিগনেচার:
@FunctionalInterface
public interface Supplier<T> {
T get();
}
এখানে:
- T: আউটপুট প্যারামিটার টাইপ।
- get(): কোনো ইনপুট গ্রহণ না করে একটি আউটপুট প্রদান করে।
Supplier Interface ব্যবহার:
Example 1:
import java.util.function.Supplier;
public class SupplierExample {
public static void main(String[] args) {
// Lambda expression implementation of Supplier
Supplier<Double> randomValue = () -> Math.random();
System.out.println(randomValue.get()); // Output: Random number between 0.0 and 1.0
}
}
এখানে:
- Supplier একটি Supplier টাইপের অবজেক্ট যা কোনো ইনপুট না নিয়ে একটি র্যান্ডম double মান প্রদান করে।
get()মেথডের মাধ্যমে আউটপুট প্রদান করা হয়।
Example 2:
import java.util.function.Supplier;
public class SupplierExample {
public static void main(String[] args) {
// Supplier for generating a default message
Supplier<String> defaultMessage = () -> "Hello, World!";
System.out.println(defaultMessage.get()); // Output: Hello, World!
}
}
এখানে:
- Supplier একটি Supplier টাইপের অবজেক্ট যা কোনো ইনপুট না নিয়ে একটি স্ট্যাটিক স্ট্রিং প্রদান করে।
Lazy Initialization with Supplier:
একটি খুব সাধারণ ব্যবহার হল lazy initialization। যখনই কোনো মান প্রয়োজন হবে, তখনই সেটা তৈরি করা হবে।
Supplier<String> lazyMessage = () -> {
System.out.println("Generating message...");
return "Lazy Message";
};
System.out.println(lazyMessage.get()); // Output: Generating message... Lazy Message
এখানে, lazyMessage শুধুমাত্র তখনই এক্সিকিউট হবে যখন get() মেথড কল হবে। এটি lazy evaluation এর একটি উদাহরণ।
Consumer এবং Supplier Interface এর মধ্যে পার্থক্য:
| Feature | Consumer | Supplier |
|---|---|---|
| Input | একটি ইনপুট গ্রহণ করে (একটি প্যারামিটার থাকে)। | কোনো ইনপুট গ্রহণ করে না (কেবল আউটপুট প্রদান করে)। |
| Output | কোনো আউটপুট প্রদান করে না (void return type)। | আউটপুট প্রদান করে (generic type T)। |
| Usage | সাধারণত সাইড-এফেক্ট সম্পাদন করতে ব্যবহৃত হয়, যেমন প্রিন্ট করা বা অবজেক্ট পরিবর্তন করা। | সাধারণত কোনো মান তৈরি বা প্রদান করতে ব্যবহৃত হয়। |
| Method | accept(T t) | get() |
| Typical Use Case | ডেটা প্রসেসিং বা সাইড-এফেক্ট তৈরি করা (যেমন, forEach()) | ডেটা তৈরি বা কোনো মান প্রদান করা (যেমন, get() বা supplier.get()) |
সারাংশ:
Consumer এবং Supplier ইন্টারফেস দুটি Java 8 এ Functional Programming এর গুরুত্বপূর্ণ অংশ।
- Consumer এমন একটি ফাংশনাল ইন্টারফেস যা ইনপুট গ্রহণ করে এবং কিছু সাইড-এফেক্ট সম্পাদন করে, যেমন প্রিন্ট করা বা ডেটাবেসে ডেটা ইনসার্ট করা।
- Supplier কোনো ইনপুট না নিয়ে আউটপুট প্রদান করে, যেমন একটি র্যান্ডম নাম্বার, অথবা স্ট্যাটিক কোনো মান।
Lambda expressions এবং method references এর মাধ্যমে Consumer এবং Supplier ব্যবহারের মাধ্যমে আপনি কার্যকরী এবং পুনরায় ব্যবহারযোগ্য কোড লিখতে পারবেন, যা আপনার অ্যাপ্লিকেশনকে আরও পরিষ্কার এবং কমপ্যাক্ট করতে সাহায্য করবে।
Java 8 থেকে Functional Programming ধারণা Java-তে যুক্ত হয়েছে এবং এর মধ্যে Functional Interfaces একটি গুরুত্বপূর্ণ ভূমিকা পালন করে। এই ইন্টারফেসগুলি lambda expressions এবং method references এর মাধ্যমে ফাংশনাল প্রোগ্রামিংয়ের সুবিধা প্রদান করে। দুইটি গুরুত্বপূর্ণ Functional Interface হল BiFunction এবং BiPredicate। এই দুটি ইন্টারফেস ব্যবহার করে আপনি দুইটি ইনপুট নিয়ে বিভিন্ন কার্যকরী এবং শর্তপূর্ণ ফাংশন তৈরি করতে পারেন।
এখানে BiFunction এবং BiPredicate ইন্টারফেসের ধারণা, ব্যবহার এবং উদাহরণ দেওয়া হয়েছে।
1. BiFunction Interface
BiFunction একটি functional interface যা দুইটি ইনপুট প্যারামিটার গ্রহণ করে এবং একটি আউটপুট প্রদান করে। এর মধ্যে দুটি ইনপুট প্যারামিটার থাকে এবং একটি রিটার্ন ভ্যালু থাকে।
BiFunction Interface-এর Syntax
@FunctionalInterface
public interface BiFunction<T, U, R> {
R apply(T t, U u);
}
- T: প্রথম ইনপুট প্যারামিটার টাইপ।
- U: দ্বিতীয় ইনপুট প্যারামিটার টাইপ।
- R: রিটার্ন টাইপ।
BiFunction Interface-এর ব্যবহার
BiFunction ব্যবহার করা হয় যখন আপনি দুটি ইনপুট প্যারামিটার গ্রহণ করে এবং তাদের উপর কোনো কাজ করেন এবং একটি ফলাফল প্রদান করেন। এটি সাধারণত lambda expression বা method reference এর মাধ্যমে ব্যবহার করা হয়।
BiFunction Example:
ধরা যাক, একটি BiFunction ব্যবহার করে দুটি সংখ্যার যোগফল বের করা:
import java.util.function.BiFunction;
public class Main {
public static void main(String[] args) {
// BiFunction to add two numbers
BiFunction<Integer, Integer, Integer> add = (a, b) -> a + b;
// Call the BiFunction
int result = add.apply(10, 20);
System.out.println("Sum: " + result); // Output: Sum: 30
}
}
এখানে, BiFunction<Integer, Integer, Integer> দুটি ইনপুট গ্রহণ করছে (প্রথম এবং দ্বিতীয় ইনপুট হল Integer) এবং একটি Integer রিটার্ন করছে (যোগফল)।
2. BiPredicate Interface
BiPredicate একটি functional interface যা দুটি ইনপুট প্যারামিটার গ্রহণ করে এবং একটি boolean মান রিটার্ন করে। এটি শর্ত যাচাই করার জন্য ব্যবহৃত হয়, যেমন predicate।
BiPredicate Interface-এর Syntax
@FunctionalInterface
public interface BiPredicate<T, U> {
boolean test(T t, U u);
}
- T: প্রথম ইনপুট প্যারামিটার টাইপ।
- U: দ্বিতীয় ইনপুট প্যারামিটার টাইপ।
BiPredicate Interface-এর ব্যবহার
BiPredicate সাধারণত এমন শর্ত যাচাই করতে ব্যবহৃত হয়, যেখানে দুটি প্যারামিটার নেওয়া হয় এবং ফলস্বরূপ true বা false রিটার্ন করা হয়। এটি সাধারণত lambda expression বা method reference এর মাধ্যমে ব্যবহৃত হয়।
BiPredicate Example:
ধরা যাক, আমরা একটি BiPredicate ব্যবহার করে দুটি সংখ্যার মধ্যে চেক করব যে, প্রথমটি দ্বিতীয়টির চেয়ে বড় কিনা:
import java.util.function.BiPredicate;
public class Main {
public static void main(String[] args) {
// BiPredicate to check if the first number is greater than the second
BiPredicate<Integer, Integer> isGreater = (a, b) -> a > b;
// Call the BiPredicate
boolean result = isGreater.test(10, 5);
System.out.println("Is 10 greater than 5? " + result); // Output: true
}
}
এখানে, BiPredicate<Integer, Integer> দুটি Integer ইনপুট গ্রহণ করছে এবং একটি boolean রিটার্ন করছে, যা যাচাই করছে প্রথম সংখ্যা দ্বিতীয় সংখ্যার চেয়ে বড় কিনা।
3. BiFunction এবং BiPredicate এর মধ্যে পার্থক্য
| Feature | BiFunction | BiPredicate |
|---|---|---|
| Purpose | Accepts two parameters and returns a result (any type). | Accepts two parameters and returns a boolean result. |
| Return Type | Can return any type of value. | Returns a boolean value. |
| Use Case | Used for operations that produce a result from two inputs. | Used for operations that test a condition on two inputs. |
| Common Operations | Arithmetic operations, string concatenation, transformations. | Checking conditions like equality, greater than, less than, etc. |
4. Chaining with BiFunction and BiPredicate
Chaining BiFunction:
BiFunction ইনপুট নিয়ে একটি আউটপুট প্রদান করে, এবং আপনি একাধিক BiFunction একত্রে চেইন করতে পারেন। andThen() বা compose() মেথড ব্যবহার করে আপনি একাধিক BiFunction একটি সাথে সংযুক্ত করতে পারেন।
import java.util.function.BiFunction;
public class Main {
public static void main(String[] args) {
BiFunction<Integer, Integer, Integer> add = (a, b) -> a + b;
BiFunction<Integer, Integer, Integer> multiply = (a, b) -> a * b;
// Chain BiFunctions
BiFunction<Integer, Integer, Integer> result = add.andThen(multiply);
// The result is first added and then multiplied
System.out.println(result.apply(2, 3)); // Output: 15 (2+3=5, 5*3=15)
}
}
এখানে andThen() মেথডটি প্রথমে যোগফল হিসাব করে তারপর গুণফল বের করে।
Chaining BiPredicate:
BiPredicate এর সাথে আপনি and(), or(), এবং negate() মেথড ব্যবহার করে শর্তগুলি চেইন করতে পারেন।
import java.util.function.BiPredicate;
public class Main {
public static void main(String[] args) {
BiPredicate<Integer, Integer> isGreaterThan = (a, b) -> a > b;
BiPredicate<Integer, Integer> isEqual = (a, b) -> a == b;
// Chaining BiPredicates
BiPredicate<Integer, Integer> combinedPredicate = isGreaterThan.and(isEqual);
// Check if 10 is greater than 5 and equal to 5
System.out.println(combinedPredicate.test(10, 5)); // Output: false
}
}
এখানে, and() মেথড দুটি শর্ত একত্রে ব্যবহার করেছে, যেখানে প্রথম শর্তে 10 > 5 সঠিক হলেও দ্বিতীয় শর্তে 10 == 5 ভুল হওয়ার কারণে ফলস্বরূপ false পাওয়া যায়।
সারাংশ
- BiFunction একটি functional interface যা দুইটি ইনপুট প্যারামিটার গ্রহণ করে এবং একটি আউটপুট রিটার্ন করে।
- BiPredicate একটি functional interface যা দুইটি ইনপুট প্যারামিটার গ্রহণ করে এবং একটি boolean রিটার্ন করে।
- BiFunction সাধারণত গাণিতিক বা ট্রান্সফর্মেশন অপারেশন পরিচালনা করতে ব্যবহৃত হয়, যখন BiPredicate শর্ত যাচাই করতে ব্যবহৃত হয়।
- Lambda expressions এবং method references ব্যবহার করে সহজেই BiFunction এবং BiPredicate এর কার্যকরী ব্যবহার করা যায়।
এই দুটি ইন্টারফেস Java Functional Programming এর গুরুত্বপূর্ণ অংশ, এবং তাদের মাধ্যমে আপনি জাভাতে আরও কার্যকরী এবং কমপ্যাক্ট কোড লিখতে পারবেন।
Read more