Java 8 Optional: NullPointerException වලට බයි බයි! Safe Code ලියන්න - SC Guide
ආයුබෝවන්! කොහොමද ඉතින් අපේ කට්ටියට? අද අපි කතා කරන්න යන්නේ Java development වලදී ඕනම කෙනෙක්ට නිතරම වගේ මුහුණ දෙන්න වෙන, හරිම හිසරදයක් වගේ වැඩක් ගැන. ඒ තමයි NullPointerException (NPE)! මම දන්නවා, මේක ඇහෙනකොටත් සමහරුන්ට සීතල වතුර ටිකක් ඔලුවට වක්කරගත්තා වගේ වෙයි. මොකද මේක කොච්චර පොඩි වෙලාවකට ආවත්, අපේ Application එකේ වැඩේ අවුල් කරන හැටි හොඳටම දන්නවනේ.
හිතන්නකෝ, අපේ කෝච්චිය නියම වෙලාවට එනවා කියලා ටිකට් අරන් ගියාම, පොඩි වැස්සකටත් පරක්කු වෙනකොට එන කේන්තිය වගේ දෙයක් තමයි මේ NullPointerException කියන්නෙත්. එක පාරටම Application එක බිඳ දාලා දානවා. ඒත් බය වෙන්න එපා! Java 8 එක්ක ආපු හරිම සුපිරි feature එකක් තියෙනවා මේ වගේ හිසරද වලින් මිදෙන්න. ඒ තමයි Optional කියන Class එක.
අද අපි මේ Guide එකෙන් බලාපොරොත්තු වෙන්නේ, Optional කියන්නේ මොකක්ද? ඒක කොහොමද අපිට මේ NullPointerException වලින් බේරෙන්න උදව් කරන්නේ කියන එක පැහැදිලිව කියලා දෙන්න. කම්මැලි කතා පැත්තකට දාලා, අපි යමු බලන්න මේ Optional magic එක කොහොමද වැඩ කරන්නේ කියලා!
NullPointerException (NPE) කියන්නේ මොකක්ද? ☠️
මුලින්ම බලමු මේ NPE කියන්නේ මොකක්ද කියලා. සරලව කිව්වොත්, අපි යම්කිසි object එකක method එකක් call කරන්න හරි, ඒකේ field එකකට access කරන්න හරි හදනකොට, ඒ object එක null නම්, ඒ කියන්නේ ඒක කිසිම value එකක් reference කරන්නේ නැත්නම්, Java Virtual Machine (JVM) එක මේ NullPointerException එක විසි කරනවා (throws an exception). මේක Java development වලදී නිතරම වගේ එන runtime error එකක්. ගොඩක් වෙලාවට මේක සිදුවෙන්නේ අපිට නොදැනීමයි, ඒ නිසා debug කරන්නත් අමාරුයි.
උදාහරණයක් විදිහට බලන්නකෝ මේ simple code snippet එක.
public class User {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public class Main {
public static void main(String[] args) {
User user = null; // මෙතනදී අපි user object එක null විදිහට assign කරනවා
String userName = user.getName(); // මෙතනදී NullPointerException එකක් එනවා
System.out.println("User Name: " + userName);
}
}උඩ තියෙන code එක run කරොත්, user.getName() කියන line එකේදී අනිවාර්යයෙන්ම NPE එකක් එනවා. මොකද user කියන object එක null නිසා. මේ වගේ දේවල් නිසා තමයි අපේ Application එක crash වෙන්නේ.
Optional කියන්නේ මොකක්ද? 📦
Java 8 එක්ක හඳුන්වා දුන්න java.util.Optional කියන්නේ null values හැසිරවීමට තියෙන සුපිරිම විසඳුමක්. සරලව කිව්වොත්, Optional කියන්නේ container object එකක්. මේ container එක ඇතුලේ non-null value එකක් තියෙන්නත් පුළුවන්, එහෙම නැත්නම් කිසිම value එකක් නැතුව empty වෙන්නත් පුළුවන්.
මේකෙන් වෙන්නේ, අපි null check කරන්න අමතක වුණාම එන NPE එක වෙනුවට, Optional එකේ value එකක් තියෙනවද නැද්ද කියලා explicitly කියන්න අපිට බල කරන එක. මේක හරියට, අපි කඩේට ගිහින් බඩු ගන්නකොට, බඩු නැත්නම් 'බඩු නෑ' කියලා කියනවා වෙනුවට, 'මේ බෑග් එක හිස්' කියලා බෑග් එක පෙන්නනවා වගේ දෙයක්. ඒක අපිට තේරෙනවා බෑග් එක හිස් කියලා, එතකොට අපිට ඊළඟට මොකද කරන්නේ කියලා තීරණය කරන්න පුළුවන්.
Optional භාවිතා කරන්නේ කොහොමද? 🤔
හරි, දැන් අපි බලමු මේ Optional එක practical විදිහට කොහොමද අපේ code එකට ගේන්නේ කියලා.
1. Optional Instances හදාගන්න විදිහ (Creating Optional Instances)
Optional.empty(): හිස්Optionalobject එකක් හදන්න මේක පාවිච්චි කරනවා.Optional.of(value): non-null value එකකින්Optionalඑකක් හදන්න මේක පාවිච්චි කරනවා. හැබැයි මතක තියාගන්න, value එකnullවුණොත් මේක NPE එකක් throw කරනවා!Optional.ofNullable(value): මේක තමයි ගොඩක් වෙලාවට පාවිච්චි වෙන්නේ. value එකnullද නැද්ද කියලා නොදන්නකොට මේක පාවිච්චි කරන්න පුළුවන්. Value එකnullනම්,Optional.empty()එකක් return කරනවා.nullනැත්නම්,Optional.of(value)වගේම වැඩ කරනවා.
String maybeName = "Sunil";
Optional<String> optionalMaybeName = Optional.ofNullable(maybeName);
String anotherMaybeName = null;
Optional<String> optionalAnotherMaybeName = Optional.ofNullable(anotherMaybeName); // Returns Optional.empty()String name = "Kamal";
Optional<String> optionalName = Optional.of(name);
// String nullName = null;
// Optional<String> optionalNullName = Optional.of(nullName); // NullPointerException here!Optional<String> emptyOptional = Optional.empty();2. Value එකක් තියෙනවද කියලා බලන විදිහ (Checking for Presence)
isPresent():Optionalඑක ඇතුලේ value එකක් තියෙනවද කියලාbooleanඑකකින් කියනවා.isEmpty()(Java 11+):Optionalඑක හිස්ද කියලාbooleanඑකකින් කියනවා.isPresent()එකේ ප්රතිලෝමය වගේ.
if (optionalAnotherMaybeName.isEmpty()) {
System.out.println("Another name is empty (Java 11+).");
}if (optionalName.isPresent()) {
System.out.println("Name is present: " + optionalName.get());
} else {
System.out.println("Name is not present.");
}3. Value එක ගන්න විදිහ (Getting the Value)
get():Optionalඑක ඇතුලේ තියෙන actual value එක return කරනවා. හැබැයි!Optionalඑක හිස් නම් මේකNoSuchElementExceptionඑකක් throw කරනවා. ඒ නිසා මේකisPresent()එක්ක විතරක් පාවිච්චි කරන්න!
Optional<String> name = Optional.of("Chamara");
String value = name.get(); // "Chamara"
// Optional<String> emptyName = Optional.empty();
// String emptyValue = emptyName.get(); // NoSuchElementException!4. Value එකක් තියෙනවා නම් මොකක් හරි කරන්න (Conditional Actions)
ifPresent(Consumer action):Optionalඑක ඇතුලේ value එකක් තියෙනවා නම්, දීපුConsumerඑක run කරනවා.ifPresentOrElse(Consumer action, Runnable emptyAction)(Java 9+): value එකක් තියෙනවා නම්actionඑක run කරනවා, නැත්නම්emptyActionඑක run කරනවා.
Optional<String> phone = Optional.ofNullable("0771234567");
phone.ifPresentOrElse(
p -> System.out.println("Phone: " + p),
() -> System.out.println("No phone number provided.")
);Optional<String> optionalEmail = Optional.ofNullable("[email protected]");
optionalEmail.ifPresent(email -> System.out.println("Email found: " + email));
Optional<String> noEmail = Optional.empty();
noEmail.ifPresent(email -> System.out.println("This won't print."));5. Default Values දෙන්න (Providing Default Values)
මේවා තමයි ගොඩක්ම ප්රයෝජනවත් methods. Value එකක් නැත්නම් default value එකක් දෙන්න මේවා පාවිච්චි කරන්න පුළුවන්.
orElse(T other):Optionalඑකේ value එකක් තියෙනවා නම් ඒක return කරනවා. නැත්නම් දීපුotherdefault value එක return කරනවා. සටහන:othervalue එකOptionalඑක empty වුණත් නැතත් හැමවෙලේම evaluate වෙනවා.orElseGet(Supplier<? extends T> supplier):Optionalඑකේ value එකක් තියෙනවා නම් ඒක return කරනවා. නැත්නම් දීපුSupplierඑක call කරලා ඒකෙන් එන value එක return කරනවා. වැදගත්:Supplierඑක call වෙන්නේOptionalඑක empty නම් විතරයි. ඒ කියන්නේ lazy evaluation.orElseThrow(Supplier<? extends X> exceptionSupplier):Optionalඑක හිස් නම් දීපුSupplierඑකෙන් එන exception එක throw කරනවා. Value එකක් තියෙනවා නම් ඒක return කරනවා.
try {
String importantValue = Optional.ofNullable(null)
.orElseThrow(() -> new IllegalArgumentException("Value must be present!"));
} catch (IllegalArgumentException e) {
System.out.println("Caught exception: " + e.getMessage());
}
String validValue = Optional.of("Valid Data")
.orElseThrow(() -> new IllegalArgumentException("Should not happen"));
System.out.println("Valid Data: " + validValue);// Imagine a heavy operation to get default value
String expensiveDefault = Optional.ofNullable(null)
.orElseGet(() -> {
System.out.println("Getting expensive default...");
return "Default User";
});
System.out.println("Expensive Default User: " + expensiveDefault);
// This will NOT print "Getting expensive default..." because value is present
String anotherUser = Optional.of("Normal User")
.orElseGet(() -> {
System.out.println("Getting expensive default...");
return "Default User";
});
System.out.println("Another User: " + anotherUser);String username = Optional.ofNullable(null).orElse("Guest");
System.out.println("Username: " + username); // Output: Guest
String activeUsername = Optional.of("Admin").orElse("Guest");
System.out.println("Active Username: " + activeUsername); // Output: Admin6. Values Transform කරන්න (Transforming Values)
map(Function<? super T, ? extends U> mapper):Optionalඑක ඇතුලේ value එකක් තියෙනවා නම්, ඒක දීපුFunctionඑකට pass කරලා, ඒකෙන් එන result එක අලුත්Optionalඑකක් විදිහට return කරනවා.flatMap(Function<? super T, ? extends Optional<U>> mapper):mapවගේමයි. හැබැයි,mapperfunction එක return කරන්නේOptionalඑකක් නම්, මේක nestedOptionalඑකක් හැදෙන්නේ නැතුව, straight upOptionalඑකක් return කරනවා. (එකම මට්ටමේ තියාගන්න).
// Imagine a method that returns Optional<Integer>
public static Optional<Integer> parseNumber(String s) {
try {
return Optional.of(Integer.parseInt(s));
} catch (NumberFormatException e) {
return Optional.empty();
}
}
Optional<String> ageString = Optional.of("30");
Optional<Integer> age = ageString.flatMap(Main::parseNumber);
System.out.println("Parsed Age: " + age.orElse(0)); // 30
Optional<String> invalidAgeString = Optional.of("abc");
Optional<Integer> invalidAge = invalidAgeString.flatMap(Main::parseNumber);
System.out.println("Invalid Age: " + invalidAge.orElse(0)); // 0Optional<String> companyName = Optional.of("softlogic");
Optional<String> upperCaseName = companyName.map(String::toUpperCase);
System.out.println("Upper Case Name: " + upperCaseName.orElse("N/A")); // SOFTLOGIC
Optional<String> emptyCompany = Optional.empty();
Optional<String> upperCaseEmpty = emptyCompany.map(String::toUpperCase);
System.out.println("Upper Case Empty: " + upperCaseEmpty.orElse("N/A")); // N/A7. Values Filter කරන්න (Filtering Values)
filter(Predicate<? super T> predicate):Optionalඑක ඇතුලේ value එකක් තියෙනවා නම්, දීපුPredicateඑකෙන් ඒක check කරනවා.trueනම්Optionalඑකේ value එක ඒ විදිහටම return කරනවා.falseනම්Optional.empty()එකක් return කරනවා.
Optional<Integer> age = Optional.of(25);
Optional<Integer> adultAge = age.filter(a -> a >= 18);
System.out.println("Adult Age: " + adultAge.orElse(0)); // 25
Optional<Integer> childAge = Optional.of(15);
Optional<Integer> filteredChildAge = childAge.filter(a -> a >= 18);
System.out.println("Filtered Child Age: " + filteredChildAge.orElse(0)); // 0 (empty optional)Optional වල වාසි මොනවද? 👍
දැන් ඔයාලට තේරෙනවා ඇති Optional එක කොච්චර වටිනවද කියලා. මෙන්න ඒකෙන් අපිට ලැබෙන ප්රධාන වාසි ටිකක්:
- NullPointerException වලින් මිදීම: මේක තමයි ප්රධානම වාසිය.
Optionalභාවිතා කිරීමෙන්,nullවිය හැකි තැන් explicit විදිහට handle කරන්න අපිට බල කරනවා. ඒ නිසා runtime එකේදී එන NPE අවදානම අඩු වෙනවා. - Code එකේ Readability එක වැඩිවීම:
if (value != null)කියලා හැම තැනම check කරනවා වෙනුවට,Optionalmethods (ifPresent,orElse,mapවගේ) පාවිච්චි කරන එකෙන් code එක වඩාත් පැහැදිලි, කියවන්න පහසු, functional style එකකට ලියන්න පුළුවන්. - API Design එක දියුණු වීම: method එකක්
Optionalඑකක් return කරනවා නම්, ඒ method එකෙන් value එකක් නැති වෙන්න පුළුවන් කියලා caller ට පැහැදිලිවම කියනවා. ඒ නිසා caller ට ඒක handle කරන්න පුළුවන්. - Functional Programming වලට උදව් වීම: Java 8 Stream API එකත් එක්ක වගේම,
Optionalඑකත් functional programming style එකට හොඳට ගැලපෙනවා.
Optional භාවිතා නොකළ යුතු තැන් ⚠️
ඕනම දෙයක් වගේ, Optional වලටත් ඒක පාවිච්චි කරන්න හොඳ නැති තැන් තියෙනවා. හැම තැනම Optional දාන එක හොඳ පුරුද්දක් නෙවෙයි. මතක තියාගන්න, Optional එක designed කරලා තියෙන්නේ method return types සඳහා මිසක්, field වලට හෝ parameters වලට නෙවෙයි.
- Class Field එකක් විදිහට:
Optionalඑක class එකක field එකක් විදිහට පාවිච්චි කරන එක එච්චර හොඳ පුරුද්දක් නෙවෙයි. ඒක serialization, persistence වගේ දේවල් වලට ගැටලු ඇති කරන්න පුළුවන්. ඒ වගේ තැන් වලටnullvalue එකක් පාවිච්චි කරන එක වඩාත් සුදුසුයි. - Method Parameter එකක් විදිහට: method එකක parameter එකක් විදිහට
Optionalඑකක් පාවිච්චි කරනවා නම්, ඒක code එකේ clarity එක අඩු කරන්න පුළුවන්. Parameter එකක්nullවෙන්න පුළුවන් නම්,nullඑක pass කරලා, method එක ඇතුලේ check කරන එක සාමාන්යයෙන් වඩා හොඳයි, නැත්නම් method overloading කරන්න පුළුවන්. - Collections ඇතුලේ:
List<Optional<String>>වගේ collections ඇතුලේOptionalobjects දාන එක එච්චර සුදුසු නෑ. ඒ වෙනුවට,nullvalues remove කරලා clean collection එකක් පාවිච්චි කරන්න පුළුවන්. - Exceptions වෙනුවට: යම්කිසි value එකක් නැතිවීම exceptional case එකක් නම් (එනම්, ඒක සාමාන්ය තත්ත්වයක් නෙවෙයි නම්),
Optional.empty()එකක් return කරනවා වෙනුවට, අදාලExceptionඑක throw කරන එක වඩාත් සුදුසුයි.
අවසාන වශයෙන් 🚀
ඉතින්, අද අපි Java 8 වල Optional කියන Class එක ගැන විස්තරාත්මකව කතා කළා. NullPointerException කියන හිසරදයෙන් මිදිලා, වඩාත් safe සහ කියවන්න පහසු Java applications develop කරන්න Optional කොච්චර උදව් වෙනවද කියලා ඔයාලට දැන් පැහැදිලි ඇති.
මතක තියාගන්න, Optional කියන්නේ මැජික් විසඳුමක් නෙවෙයි, හැබැයි හරි විදිහට පාවිච්චි කරොත්, ඒක ඔයාලගේ code base එක ගොඩක් දියුණු කරන්න පුළුවන්. අද ඉඳන්ම ඔයාලගේ project වල Optional භාවිතා කරලා බලන්න. ඒක ඔයාලගේ developer ජීවිතේට ලොකු සහනයක් වෙයි!
මේ ගැන ඔයාලගේ අදහස්, ප්රශ්න, හෝ ඔයාලගේ අත්දැකීම් පහළින් comment කරන්න. අපි ඊළඟ ලිපියෙන් හම්බවෙමු! තව සුපිරිම Java concept එකක් අරගෙන එන්නම්.
ජය වේවා!