September 10, 2016

Optionals

ရှေ့အခန်း Streams API ကို ရေးသားစဥ် Method တွေရဲ့ Return Type တွေမှာ java.util.Optional Object ကို အသုံးပြုနေတာကို တွေ့ရပါမယ်။ ဒီအခန်းမှာတော့ Optional အကြောင်းကို လေ့လာသွားပါမယ်။

Optional Class ဟာ တန်ဖိုးတစ်ခုကို သိမ်းပေးထားနိုင်တဲ့ Container Class ဖြစ်ပါတယ်။ သို့ပေမဲ့ တစ်ခါသိမ်းပေးထားတဲ့ တန်ဖိုးကို ပြောင်းလို့မရနိုင်ပါဘူး။ အဲ့ဒီအတွက် Immutable Class အမျိုးအစားဖြစ်ပါတယ်။

ထူးခြားချက်ကတော့ တန်ဖိုးရှိသလား မရှိသလားဆိုတဲ့ အပေါ်မှာမူတည်ပြီး လုပ်ဆောင်မှူ့တွေကို ပြောင်းပေးနိုင်တဲ့ အချက်ဖြစ်ပါတယ်။ အဲ့ဒီအတွက် ယခင် တန်ဖိုးဟာ Null ဖြစ်သလား မဖြစ်သလားဆိုတာကို if နဲ့ စစ်ပြီး လုပ်ဆောင်မှု့တွေကို ပြောင်းရေးနေတဲ့ နေရာမှာ အဲ့ဒီထူးခြားချက်ကို အသုံးပြုနိုင်မှာ ဖြစ်ပါတယ်။

ရေးသားနေတဲ့ ပရိုဂရမ်တွေမှာ Null Check ကို မေ့ပြီး မရေးမိရင် NullPointerException ကို Throw ဖြစ်နိုင်ပါတယ်။ ဒါပေမဲ့ နေရာတကာမှာ Null Check တွေ ရေးနေရတာဟာ တကယ်ကို အလုပ်ရှုပ်ပြီး Program Logic ကိုလဲ​ရှုပ်ထွေးစေပြီး၊ Code တွေကို ဖတ်တဲ့နေရာမှာလဲ​ ရှူပ်ထွေးစေပါတယ်။ ဒီ့အပြင် Test Case တွေကို ရေးသားတဲ့ နေရာမှာလဲ​ Null အတွက်နဲ့ Null မဟုတ်တဲ့အတွက် ဆိုပြီး ပိုရေးရမှာ ဖြစ်ပါတယ်။

ဒီနေရာမှာ အသုံးဝင်တာကတော့ Optional ဖြစ်ပါတယ်။

Optional Class ဟာလဲ Stream တွေလိုပဲ Generics Type အမျိုးအစားဖြစ်ပြီး၊ အသုံးပြုမဲ့ Type ကို Type Parameter အနေနဲ့ ရေးသားပေးရမှာ ဖြစ်ပါတယ်။ အဲ့ဒီအတွက် primitive type တွေဖြစ်တဲ့ int, double အစရှိသည်တို့ကို အသုံးပြုနိုင်မှာ မဟုတ်ပါဘူး။ အဲ့ဒီအစား Primitive Type တွေ အတွက် OptionalInt, OptionalDouble, OptionalLong Class တို့ကိုပြင်ထားပါတယ်။

ဥပမာအနေနဲ့ int [] တစ်ခုရဲ့ ပျမ်းမျှကို ရှာတဲ့ ပရိုဂရမ်တစ်ခုကို စဉ်းစားကြည့်ပါမယ်။ တကယ်လို့ IntStream Object ရဲ့ average method ကို သုံးမယ်ဆိုရင် Source ဖြစ်တဲ့ int [] က blanck ဖြစ်နေရင် Average တန်ဖိုးကို ရှာနိုင်မှာ မဟုတ်ပါဘူး။ အဲ့လို အခါမျိုးမှာ ပျမ်းမျှတန်ဖိုးကို 0.0 လို့ပြန်ပေးရမှာ ဖြစ်ပါတယ်။

လက်တွေ့ရေးကြည့် ရအောင်။



အထက်ပါ ကုဒ်ရဲ့ လိုင်းနံပါတ် ၉ မှာ၊ IntStream Object ရဲ့ average method ကို ခေါ်မယ်ဆိုရင် OptionalDouble Object ကို ပြန်ရပါမယ်။ Average တန်ဖိုးကိုပိုင်တဲ့ Optional အမျိုးအစား Object ဖြစ်ပါတယ်။ OptionalDouble ရဲ့ getAsDouble method ကို သုံးပြီး double တန်ဖိုးကို ထုတ်ယူနိုင်ပါတယ်။

ဒါပေမဲ့ အထက် နမူနာထဲမှာလို Array အလွတ်ကို ပေးပြီး Avreage တန်ဖိုးကို ရှာမယ်ဆိုရင်တော့ Average တန်ဖိုးကို ရရှိမှာ မဟုတ်ပါဘူး။ အဲ့ဒီအတွက် Average ကို ဖေါ်ပြမည့် တန်ဖိုးလဲ​ပါမှာ မဟုတ်ပါဘူး။ အဲဒီလို အခါမျိုးမှာ getAsDouble ကို သုံးရင် NoSuchElementException ကို ဖြစ်ပွားစေမှာပါ။

အဲ့ဒီလို အခါမျိုးမှာ Line 10 မှာ ရေးထားသလို orElseGet ကို အသုံးပြုမယ်ဆိုရင် တန်ဖိုးမပါရင် 0.0 ကို ပြန်ပေးမယ်ဆိုပြီး သတ်မှတ်လို့ရပါတယ်။ ဤနည်းအားဖြင့် တန်ဖိုးမရှိတဲ့ အခါမှာ ဖြစ်ပွားတတ်တဲ့ ပြဿနာများကို ရှောင်ရှားနိုင်ပါမယ်။ Optional Class မှာ အထက်ပါ Function များ အပြင် အခြား အသုံးဝင်သော Feature များကိုလဲ​ပြင်ဆင် ထားပါသေးတယ်။ ဆက်လက်ပြီး လေ့လာကြည့်ကြရအောင်။


Creation

Optional Object ကို ရယူလိုတဲ့ အခါမှာ အောက်ပါ Static Method များကို အသုံးပြုနိုင်ပါတယ်။

Method
Description
empty()
တန်ဖိုး မပါသော Optional Object ကို instantiate လုပ်လိုတဲ့ အခါမှာ အသုံးပြုနိုင်ပါတယ်
of(T value)
null မဟုတ်သော တန်ဖိုးကို ပိုင်ဆိုင် စေလိုသော Optional Object ကို instantiate လုပ်လိုတဲ့ အခါမှာ အသုံးပြုနိုင်ပါတယ်
ofNullable(T value)
null လဲ ဖြစ်နိုင်သလို တန်ဖိုးလဲ ရှိခြင်ရှိနိုင်သော Optional Object ကို Instantiate လုပ်လိုတဲ့ အခါမှာ အသုံးပြုနိုင်ပါတယ်

empty() ကို သုံးရင်တော့ တန်ဖိုးမပါသော အလွတ် Optional Object တစ်ခုကို ဆောက်လုပ်နိုင်မှာ ဖြစ်ပါတယ်။ တန်ဖိုးကို သတ်မှတ်ပြီး Optional Object ကို ဆောက်လုပ်ချင်တယ်ဆိုရင်တော့ of နဲ့ ofNullable method တို့ကို သုံးနိုင်မှာဖြစ်ပါတယ်။ သူတို့ နှစ်ခုကွာခြားတာကတော့ of method ရဲ့ Argument ကို null သတ်မှတ်လို့ မရတာဖြစ်ပါတယ်။ တကယ်လို့ of method ရဲ့ Argument ကို null လို့သတ်မှတ်မိပါက NullPointerException ကို ဖြစ်ပေါ်စေမှာ ဖြစ်ပါတယ်။

OptionalInt, OptionalDouble နဲ့ OptionalLong တို့မှာလဲ အလားတူ Method များကို ပံ့ပိုးပေးထားသော်လဲ၊ Primitive Type တွေမှာ null တန်ဖိုးဟာ ရှိစရာ အကြောင်းမရှိတဲ့ အတွက် ofNullable method ကိုတော့ ရေးသားမထားပါဘူး။


Getting Values


Optional Object က ပိုင်ဆိုင်တဲ့ တန်ဖိုးကို ရယူနိုင်ဖို့အတွက် Method ၄ မျိုး ပြင်ဆင်ထားပါတယ်။ Primitive တွေ အတွက် Optional တွေမှာတော့ getAsDouble တို့လို သီးခြား Method တွေကိုလဲ​ပြင်ဆင်ထားပါတယ်။

Method
Description
get()
ပိုင်ဆိုင်တဲ့ တန်ဖိုးကို Return လုပ်ပါမယ်။ တကယ်လို့ တန်ဖိုးမရှိခဲ့ရင် NoSuchElementException ကို ဖြစ်ပေါ်စေပါမယ်။
orElse(T value)
ပိုင်ဆိုင်တဲ့ တန်ဖိုးကို Return လုပ်ပါမယ်။ တကယ်လို့ တန်ဖိုးမရှိခဲ့ရင် Argument မှာ ရေးသားထားတဲ့ Value ကို Default တန်ဖိုး အနေနဲ့ Return လုပ်ပါမယ်။
orElseGet(Supplier<? extends T> supplier)
ပိုင်ဆိုင်တဲ့ တန်ဖိုးကို Return လုပ်ပါမယ်။ တကယ်လို့ တန်ဖိုးမရှိခဲ့ရင် Argument မှာ ရေးသားထားတဲ့ Supplier ကနေ ထုတ်ပေးမဲ့ တန်ဖိုး ကို Default တန်ဖိုး အနေနဲ့ Return လုပ်ပါမယ်။
Functional Interface
ဖြစ်တဲ့အတွက် Lambda Expression နဲ့ လဲ ရေးသားနိုင်ပါမယ်။
<X extends Throwable> orElseThrow(
   Supplier<? extends X> exceptionSupplier)
ပိုင်ဆိုင်တဲ့ တန်ဖိုးကို Return လုပ်ပါမယ်။ တကယ်လို့ တန်ဖိုးမရှိခဲ့ရင် Argument မှာ ရေးသားထားတဲ့ Supplier ကနေ ထုတ်ပေးမဲ့ တန်ဖိုး ကို Exception ကို Throw လုပ်ပါမယ်။

နမူနာ အနေနဲ့ ကျောင်းသား အမှတ် စာရင်းထဲကနေ အနိမ့်ဆုံး အမှတ်ရသူ ကျောင်းသားကို ရှာဖွေပြီး၊ အဲ့ဒီကျောင်းသားရဲ့ ပျမ်းမျှ အမှတ်ဟာ ၄၀ အောက်ကို ရောက်နေရင် Runtime Exception ကို ဖြစ်ပေါ်စေတဲ့ Program တစ်ခုကို ရေးကြည့်ပါမယ်။

အထက်ပါ နမူနာမှာ ကျောင်းသား List ကို Stream အဖြစ်ပြောင်းပြီး၊ Average အမှတ် ငယ်ရာမှ ကြီးရာသို့ Sort လုပ်ခိုင်းပါတယ်။ ပြီးနောက် အရင်ဆုံးတွေ့တဲ့ Student ကို ရှာခိုင်းပါတယ်။ findFirst method ရဲ့ Return Type ဟာ Optional<Student> ဖြစ်ပါတယ်။ တဖန် အမှတ် 40 နှင့် အထက်ဖြစ်သလားကို စစ်ပါတယ်။ တကယ်လို့ မဟုတ်ခဲ့ရင် Optional မှာ Student ကို သိမ်းပေးထားမှာ မဟုတ်ပါဘူး။

အဲ့ဒီလို အခါမျိုးမှာ RuntimeException ကို ဖြစ်ပေါ်စေမည့် Supplier ကို ရေးသားထားခြင်းအားဖြင့် တကယ်လို့ Average အမှတ် အနည်းဆုံးကျောင်းသားရဲ့ Average အမှတ်ဟာ ၄၀ အောက်ကို ရောက်နေရင် RuntimeException ကို ဖြစ်ပေါ်စေမှာ ဖြစ်ပါတယ်။


Other Methods

Method
Description
ifPresent(Consumer<T> consumer)
တန်ဖိုးရှိပါက အဆိုပါတန်ဖိုးအား အသုံးပြုပြီး လုပ်ဆောင်နိုင်ရန် Consumer Interface Object ကို ရယူနေပါတယ်။ 
filter(Predicate<T> predicate)
တန်ဖိုးအား စစ်ပေးနိုင်ရန် အတွက် Predicate Interface ကို ရယူနေပါတယ်။ Predicate နဲ့ စစ်လိုက်တဲ့ ရလဒ်ဟာ True ဖြစ်ပါက တန်ဖိုးပါတဲ့ Optional ကို ရရှိမှာ ဖြစ်ပြီး၊ False ဆိုရင်တော့ တန်ဖိုးမပါတဲ့ Optional ကို ရရှိမှာ ဖြစ်ပါတယ်
map(Function<? supper T, ? extend U> mapper)
Optional တစ်ခုမှ အခြားသော တန်ဖိုး အမျိုးအစားကို ပိုင်သော Optional အဖြစ်ပြောင်းပေးနိုင်ပါတယ်
flatMap(Function<T, Optional<U>> mapper
map နှင့် ဆင်တူသော်လည်း၊ Function ရဲ့ ပြောင်းပေးရမဲ့ ပုံစံက အခြားသော Optional တစ်ခု ဖြစ်နေခြင်း ဖြစ်ပါတယ်


Optional ကို ဘယ်လိုနေရာတွေမှာ သုံးမှာလဲ

Optional တွေဟာ Null Check အတွက်ရေးသားရတဲ့ ကုဒ်တွေကို ရှောင်ရှားနိုင်အောင် ရေးသားဖို့ အတွက် အသုံးဝင်ကြောင်းကို ရှေ့မှာလဲ ဖေါ်ပြခဲ့ပြီးပါပြီ။ ဒါပေမဲ့ တွေ့ကရာနေရာမှာ Optional ကို သုံးလို့ကောင်းတာ မဟုတ်ပါဘူး။ ကဲ ဒါဖြင့် ဘယ်လိုနေရာတွေမှာ သုံးကြမလဲ။

ပုံမှန်အားဖြင် Java မှာ Variable ၅ မျိုးရှိပါတယ်။ အဲ့ဒါတွေကတော့ Local Variable, Instance Variable, Static Variable, Method Arguments နဲ့ Return Value တို့ဖြစ်ကြပါတယ်။

အဲ့ဒီအထဲမှာ Local Variable, Instance Variable နဲ့ Static Variable တွေမှာ Optional ကို သုံးတာဟာ ဘာမှ ထူးခြားမှာ မဟုတ်ပါဘူး။ Optional ရဲ့ကောင်းကွက်ကို အသုံးချနိုင်မှာ မဟုတ်ပါဘူး။

ကျွန်တော့် အနေနဲ့ကတော့ Optional ကို သုံးမယ်ဆိုရင် Method Return Type မှာ သုံးသင့်တယ်လို့ထင်ပါတယ်။ ဒါမှ အဲ့ဒီ Method ကို သုံးတဲ့ နေရာမှာ Null Check ကို ရေးစရာမလိုပဲ Logic တွေကို ခွဲပြီး ရေးနိုင်မှာ ဖြစ်ပါတယ်။ Streams API မှာလဲ null ကို return ပြန်နိုင်တဲ့ Method တွေရဲ့ Return Type နေရာမှာ Optional ကို သုံးထားတာကို တွေ့ရပါတယ်။

ဒါကြောင့်မို့လို့ null ကို ပြန်စရာ အကြောင်းမရှိတဲ့ Method တွေရဲ့ Return Type တွေမှာ Optional ကို သုံးစရာ အကြောင်းမရှိပါဘူ။

Reference
http://itpro.nikkeibp.co.jp/atcl/column/15/120700278/032200009/

ဆက်ပါဦးမယ်။
မင်းလွင်



1 comment: