Java වල Lambda Expressions: කේතකරණය සරල කරන අපූරු ක්රමයක්


ආයුබෝවන් හිතමිත්ර Java ලෝලීන්ට! අද අපි කතා කරන්නේ Java 8 සමඟ හඳුන්වා දුන්, කේතකරණය වඩාත් සංක්ෂිප්ත (concise) සහ ප්රකාශනාත්මක (expressive) කරන ප්රබල අංගයක් වන Lambda Expressions ගැනයි. සරලව කිව්වොත්, මේවා නම් රහිත, කෙටි කේත කොටස් (short code blocks) වන අතර, ඒවා වෙනත් method එකකට argument එකක් ලෙස යැවීමට හෝ දත්තයක් ලෙස භාවිතා කිරීමට හැකියාව ලබා දෙනවා.
Lambda Expressions කියන්නේ මොනවාද?
හිතන්න ඔබට යම් කාර්යයක් කිරීමට අවශ්ය කුඩා කේත ඛණ්ඩයක් තියෙනවා, උදාහරණයක් විදියට list එකක තියෙන හැම අයිතමයක්ම print කරන එක. සාමාන්යයෙන් අපි මේකට වෙනම method එකක් ලියනවා හෝ anonymous inner class එකක් පාවිච්චි කරනවා. Lambda expression එකකින් මේ කාර්යය බොහොම කෙටියෙන්, අවශ්ය තැනම ලියන්න පුළුවන්.
Lambda expression එකක් කියන්නේ:
නමක් නැති ශ්රිතයක් (Anonymous Function): එයට නිශ්චිත නමක් නැහැ.
ක්රියාකාරීත්වයක් (Functionality): යම් කාර්යයක් ඉටු කරන කේතයක්.
Argument එකක් ලෙස යැවිය හැකි: Method එකකට parameter එකක් විදියට දෙන්න පුළුවන්.
Data එකක් ලෙස සැලකිය හැකි: කේතයක් වුනත්, දත්තයක් වගේ හසුරවන්න පුළුවන් කේත කොටසකට.
මේවා හඳුන්වාදීමේ ප්රධාන අරමුණක් වුනේ Java වලට Functional Programming සංකල්ප පහසුවෙන් එක් කිරීමට සහ විශේෂයෙන්ම Java Streams API වැනි දේ සමඟ වැඩ කිරීම සරල කිරීමටයි.
Lambda සහ Functional Interfaces (Single Abstract Method Interfaces - SAM)
Lambda expressions හි හදවත තමයි Functional Interface කියන්නේ.
Functional Interface යනු: හරියටම එක abstract method එකක් පමණක් අඩංගු වන interface එකකි. (මේවා
@FunctionalInterface
annotation එකෙන් සලකුණු කිරීම හොඳ පුරුද්දක් වුනත්, අනිවාර්ය නැහැ). උදාහරණ:Runnable
(එහිrun()
method එක පමණයි),ActionListener
(එහිactionPerformed()
method එක පමණයි),Comparator
(එහිcompare()
method එක පමණයි).Lambda එක ගැළපෙන්නේ කෙසේද?: ඔබ ලියන Lambda expression එක ඇත්තටම අර Functional Interface එකේ තියෙන එකම එක abstract method එකට අදාළ implementation (ක්රියාත්මක වන කේතය) එකයි. Lambda එකේ parameters ගණන, වර්ගය සහ return type එක අර abstract method එකට ගැළපෙන්න ඕන.
පහළ තියෙන උදාහරණය බලල ඉන්නකෝ
// Functional Interface එකක් (එක abstract method එකක්)
@FunctionalInterface
interface MyFunctionalInterface {
void execute(); // එකම abstract method එක
}
// තව Functional Interface එකක්
@FunctionalInterface
interface StringOperation {
String operate(String s1, String s2);
}
public class LambdaExample {
public static void main(String[] args) {
// Lambda expression එකක් MyFunctionalInterface එක implement කරනවා
MyFunctionalInterface greeting = () -> System.out.println("Hello Lambda!");
greeting.execute(); // Lambda එක execute කිරීම
// Lambda expression එකක් StringOperation එක implement කරනවා
StringOperation concat = (str1, str2) -> str1 + str2;
String result = concat.operate("Java ", "Lambdas");
System.out.println(result); // ප්රතිඵලය: Java Lambdas
}
}
Lambda Expression Syntax
Lambda expression එකක මූලික කොටස් තුනයි:
Parameter List (පැරාමීටර ලැයිස්තුව):
()
වරහන් තුල. Abstract method එකේ parameters වලට අනුරූප වෙනවා.Arrow Token (ඊතල සලකුණ):
->
Body (කේත කොටස):
{}
වරහන් තුල statements කිහිපයක් හෝ වරහන් නැතුව එක expression එකක්.
(parameters) -> { body }
හෝ (parameters) -> expression
Lambda Parameters (පැරාමීටර)
Zero Parameters (පැරාමීටර රහිත): Abstract method එකට parameters නැත්නම්, හිස් වරහන්
()
යොදනවා.Runnable r = () -> System.out.println("Running...");
One Parameter (එක පැරාමීටරයක්):
Parameter type එක compiler එකට infer කරන්න පුළුවන් නම්, වරහන්
()
අත්යවශ්ය නැහැ.Consumer<String> printer = msg -> System.out.println(msg); printer.accept("Hello!");
Type එක සඳහන් කරනවා නම් හෝ වරහන් යොදනවා නම්:
Consumer<String> printerExplicit = (String msg) -> System.out.println(msg);
Multiple Parameters (පැරාමීටර කිහිපයක්): Parameters කොමාවෙන් වෙන් කර
()
වරහන් අනිවාර්යයෙන් යොදනවා.BinaryOperator<Integer> adder = (a, b) -> a + b; System.out.println(adder.apply(5, 3)); // ප්රතිඵලය: 8
Parameter Types (පැරාමීටර වර්ග): Compiler එකට බොහෝවිට parameter types infer කරන්න පුළුවන්. නමුත් අවශ්ය නම් පැහැදිලිව සඳහන් කරන්නත් පුළුවන්.
BinaryOperator<Integer> adderTyped = (Integer a, Integer b) -> a + b;
var
Parameter Types (Java 11 සිට): Parameters වල type එකvar
keyword එකෙන් declare කරන්න පුළුවන්. මේක විශේෂයෙන් parameter එකකට annotation එකක් යොදන විට ප්රයෝජනවත්.// Java 11+ BiFunction<String, String, String> concatVar = (var s1, var s2) -> s1 + s2;
Lambda Function Body (ක්රියාත්මක වන කේත කොටස)
Expression Body: Body එකේ තියෙන්නේ එකම එක expression එකක් නම්,
{}
වරහන් අවශ්ය නැහැ. ඒ expression එකේ ප්රතිඵලය තමයි lambda එකෙන් return වෙන්නේ (abstract method එක return type එකක් බලාපොරොත්තු වෙනවා නම්). JavaBinaryOperator<Integer> multiplier = (a, b) -> a * b; // return a * b implicitly
Block Body: Body එකේ statements එකකට වඩා තියෙනවා නම්,
{}
වරහන් යොදන්න ඕන. Abstract method එක return type එකක් බලාපොරොත්තු වෙනවා නම්,return
keyword එක අනිවාර්යයෙන් භාවිතා කරන්න ඕන. JavaBinaryOperator<Integer> complexAdder = (a, b) -> { System.out.println("Adding " + a + " and " + b); int sum = a + b; return sum; // Explicit return };
Lambda Type Inference
Compiler එක විසින් Lambda expression එක ගැළපෙන්නේ කුමන Functional Interface එකටද කියා එය භාවිතා වන සන්දර්භය (context) අනුව තීරණය කරනවා. උදාහරණයක් ලෙස, lambda එකක් variable එකකට assign කරන විට හෝ method එකකට argument එකක් ලෙස pass කරන විට, ඒ variable එකේ හෝ parameter එකේ type එක (Functional Interface එක) අනුව lambda එකේ type එක infer කරගන්නවා.
Lambda Expressions vs. Anonymous Interface Implementations
Lambda expressions කියන්නේ බොහෝවිට Functional Interface එකක් implement කරන Anonymous Inner Class එකක් ලියන එකට වඩා සරල, කෙටි ක්රමයක්.
// Anonymous Inner Class ක්රමය
Runnable r1 = new Runnable() {
@Override
public void run() {
System.out.println("Running via Anonymous Class");
}
};
// Lambda ක්රමය (වඩා කෙටියි)
Runnable r2 = () -> System.out.println("Running via Lambda");
ප්රධාන වෙනසක් තමයි this
keyword එක හැසිරෙන විදිය. Anonymous class එකක this
කියන්නේ anonymous class instance එකට. Lambda expression එකක this
කියන්නේ එය අඩංගු වන පන්තියේ (enclosing class) instance එකටයි. Lambda එක තමන්ගේම scope එකක් this
සඳහා හදන්නේ නැහැ.
Variable Capture (විචල්ය ග්රහණය)
Lambda expressions වලට තමන් නිර්වචනය කර ඇති scope එකෙන් (enclosing scope) පිටත තියෙන variables access කරන්න පුළුවන්. මේකට කියන්නේ "Variable Capture" කියලා.
Local Variable Capture: Lambda එකකට එය අඩංගු වන method එකේ local variables access කරන්න පුළුවන්, නමුත් ඒ variable එක
final
වෙන්න ඕන හෝ effectively final වෙන්න ඕන. Effectively final කියන්නේ, variable එක declare කළාට පස්සේ එහි අගය කොතනකවත් වෙනස් කරන්නේ නැති එකයි.String prefix = "Msg: "; // effectively final Consumer<String> printerWithPrefix = msg -> System.out.println(prefix + msg); // prefix = "New: "; // මෙහෙම කළොත් උඩ lambda එක compile වෙන්නේ නෑ. printerWithPrefix.accept("Hello!");
Instance Variable Capture: Enclosing class එකේ instance variables (non-static) access කරන්න පුළුවන්.
this
keyword එක හරහා access කරනවා වගේ.Static Variable Capture: Enclosing class එකේ static variables access කරන්න පුළුවන්.
Lambdas as Objects
Lambda expression එකක් execute වුනාම, එය ඇත්තටම අදාළ Functional Interface එකේ object instance එකක් නිර්මාණය කරනවා. ඒ නිසා lambda expressions objects වගේ assign කරන්න, pass කරන්න පුළුවන්.
Method References (ශ්රිත යොමු)
සමහර වෙලාවට Lambda expression එකකින් කරන්නේ තියෙන method එකක් call කරන එක විතරයි. ඒ වගේ අවස්ථා වලදී කේතය තවත් කෙටි කරන්න Method References පාවිච්චි කරන්න පුළුවන්. මේක lambda එකට syntax sugar එකක්.
Static Method References:
ClassName::staticMethodName
// Lambda: (s) -> Integer.parseInt(s) // Method Reference: Function<String, Integer> parser = Integer::parseInt; System.out.println(parser.apply("123"));
තවත් උදාහරණයක්:
System.out::println
List<String> names = Arrays.asList("Alice", "Bob", "Charlie"); // Lambda: name -> System.out.println(name) // Method Reference: names.forEach(System.out::println);
Instance Method References :
objectReference::instanceMethodName
String sample = "Hello"; // Lambda: () -> sample.length() // Method Reference: Supplier<Integer> lengthSupplier = sample::length; System.out.println(lengthSupplier.get()); // Output: 5
Instance Method References (ඕනෑම object එකක):
ClassName::instanceMethodName
- මෙහිදී lambda එකේ පළවෙනි parameter එක තමයි method එක call කරන object එක වෙන්නේ.// Lambda: (s) -> s.toUpperCase() // Method Reference: Function<String, String> upperCaser = String::toUpperCase; System.out.println(upperCaser.apply("java")); // Output: JAVA
Constructor References:
ClassName::new
- අලුත් objects හදන්න.// Lambda: () -> new ArrayList<String>() // Method Reference: Supplier<List<String>> listSupplier = ArrayList::new; List<String> myList = listSupplier.get();
Interfaces With Default and Static Methods
Java 8 සිට interface වලට default
සහ static
methods අඩංගු වෙන්න පුළුවන්. නමුත් Functional Interface එකක් වෙන්න නම් තවමත් එකම එක abstract method එකක් පමණක් තිබිය යුතුයි. Default සහ static methods තිබීම lambda compatibility එකට බලපාන්නේ නැහැ.
සමාලෝචනය
Java Lambda Expressions කියන්නේ කේතය වඩාත් සංක්ෂිප්ත, කියවීමට පහසු, සහ functional programming ශෛලියට අනුගත කිරීමට උපකාරී වන ප්රබල මෙවලමක්. විශේෂයෙන්ම Collections සහ Streams API සමඟ වැඩ කිරීමේදී අතිශයින් ප්රයෝජනවත්. Functional Interfaces, Variable Capture, සහ Method References වැනි සංකල්ප තේරුම් ගැනීමෙන් ඔබට Lambda Expressions වල සම්පූර්ණ ප්රයෝජනය ලබාගන්න පුළුවන්.
ඔබේ Java කේතකරණයේදී Lambda Expressions භාවිතා කර එහි වාසි අත්විඳින්න! ජය 🤍!
Subscribe to my newsletter
Read articles from Uthsara Basnayake directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
