استثناء اشاره گر به تهی (NullPointerException)
این استثناء زمانی رخ میدهد که به متغیری دسترسی پیدا میکنیم که به هیچ آبجکتی اشاره نمیکند. مثلا :
String javaPro=null; if(javaPro.equals("Best")){ System.out.println(true); } |
به دلیل اینکه String تعریف شده به هیچ ابجکتی اشاره نمیکند(اصطلاحا new نشده) استثنای nullpointerException روی میدهد.
این استثنا زیر کلاسی از RuntimeException است بنابراین از نوع unchecked exception (استثنای چک نشده) است.
راه حل : استفاده از کلاس Optional
کلاسی است که در جاوا ۸ معرفی شده و هدف ان جلوگیری از استثناء معروف nullpointerexception است(هرچند گویا به طور کامل از چنین خطایی نتوانسته جلوگیری کند)
میتوان گفت بیشتر ابزاری است برای راحت تر چک کردن مقادیر تهی و راحت تر گریختن از خطای nullpointerexception
بیگمان هرکس که با جاوا کد زده باشد با خطای nullpointerexception اشنا است.
فرض کنید کلاسی به نام Person داریم و در ان یک فیلد به نام name و یک Getter برای آن داریم:
public class Person {
private String name; public String getName() { return name; } } |
حال در کلاس main برنامه ی خود یک متد داریم به این صورت:(فرض کنید این متد بر اساس یکسری عملیات به مقدار null رسیده است..مثلا Person را از دیتابیس خوانده و هیچ مقداری پیدا نکرده است
}()public static Person getPerson return null; } |
طبعا هنگامی که در main چنین خط کدی را بنویسیم :
public static void main(String[] args) { getPerson().getName(); } |
به خطای nullpointerexception برمیخوریم.
حال راه حل جاوا هشت برای گریز از این خطا چیست؟
ابتدا توضیحاتی اندک ولی کارامد درمورد متد های کلاس Optional میدهیم:
کلاس اپشنال از دیزاین پترن Factory استفاده میکند(اجازه ی ساخت شی با کلیدواژه new از ان را نداریم)
Optional.empty(); Optional.of(T t); Optional.ofNullable(T value); |
متد اول به جای همان مثال بالا که null برگشت دادیم استفاده میشود.
متد دوم یک مقدار میپذیرد با این شرط که تهی نباشد و متد سوم میتواند مقداری تهی نیز بپذیرد.(در صورتی که مقدار تهی باشد یک Optional.empty برمیگرداند )
حال مثال اول را اینگونه بازنویسی میکنیم:
در اغاز بایستی نوع بازگشتی متد را از جنس Optionalتعریف کنیم:
public static Optional<Person> getPerson() {
return Optional.empty();
} |
نکته : در کد بالا شرایط قبلی برقرار است. یعنی مثلا در پایگاه داده، داده ی مورد نظر پیدا نشده و ما به جای null از کلاس Optional بعنوان نوع برگشتی استفاده میکنیم.
حال در متد main متد getPerson را call میکنیم:
public static void main(String[] args) {
Optional<Person> op=getPerson();
System.out.println(op);
} |
خواهیم دید که دیگر خبری از ارور nullpointerexception نیست بلکه چنین چیزی در خروجی مشاهده میکنیم:
Optional.empty
|
سایر متد های سودمند کلاس Optional
orElse(T t);
|
اگر مقدار وجود داشت...مقدار Tرا برگشت میدهد وگرنه مقداری که در ورودی متد داده اید را برگشت میدهد(دیگر مقدار null درکار نخواهد بود)
مثال :
public class Main { public static void main(String[] args) { Student student = Optional.ofNullable(getStudentWithName("hamza")).orElse(new Student("no one", 0, "Unknown")); System.out.println(student.getName()); } public static Student getStudentWithName(String name ){ // lets suppose that our database contain only 2 students ahmed and hamza . if (name.equals("hamza") || name.equals("ahmed")) { return new Student(name, 22, "Morocco"); } else { return null ; } } } |
ممکن است اگر مقداری وجود نداشت بخواهید استثنایی به راه بیاندازید :
orElseThrow(T t)
|
متدی است که برای ایجاد یک استثنا در صورت خالی بودن Optional کاربرد دارد. برخلاف orElse که یک مقدار پیشفرض را برمیگرداند، orElseThrow به شما اجازه میدهد یک استثناء ایجاد کنید.
مثال :
public class Main { public static void main(String[] args) throws StudentNotFoundException { Student student = Optional.ofNullable(getStudentWithName("fs")).orElseThrow(()-> new StudentNotFoundException("the Student is not Present ")); System.out.println(student.getName()); } public static Student getStudentWithName(String name ){ // lets suppose that our database contain only 2 students ahmed and hamza . if (name.equals("hamza") || name.equals("ahmed")) { return new Student(name, 22, "Morocco"); } else { return null ; } } } |
متد دیگری که بسیار مهم است ( زمانی که بخواهیم قبل از دریافت مقدار درون Optional چک کنیم که دقیقا میداری دارد یا خیر)
isPresent();
|
گر مقداری داخل اپشنال بود true برگشت میدهد اگر نبود false
get();
|
اگر مقدار وجود داشت برگشت میدهد و اگر نبود nosuchelementexception برگشت میدهد(و بازهم خبری از ارور null نیست)
ifPresent(Consumer<? super T> consumer)
|
اگر مقدار وجود داشت عملیات مورد نظرمان انجام شود
ifPresentOrElse(Consumer<? super T> consumer,Runnable run)
|
اگر مقدار وجود داشت عملیات مورد نظر انجام شود و اگر تهی بود متد run از اینترفیس Runnable صدا زده میشود
و بسیار متد های سودمند دیگر
توجه داشته باشید Runnable در بالا ربطی به ترد ندارد و صرفا بخاطر متد run از ان استفاده شده است
چه زمانی از Optional استفاده نکنیم؟
مثال :
private String firstName; public Optional<String> getFirstName() { return Optional.ofNullable(firstName); } public void setFirstName(String firstName) { this.firstName = firstName; } |
در چنین شرایطی اگر خروجی جیسون داشته باشید مشاهده خواهید کرد:
{"firstName":{"present":true}}
درحالی که باید مثلا چنین خروجی مشاهده میکردید:
{"firstName":"JavaPro"}
یکی از کاربرد های خیلی خوب کلاس Optional در Spring data jpa است. زمانی که یک Repository دارید میتوانید بعنوان Return type از کلاس Optional استفاده کنید(مزیت های خیلی زیادی دارد). مثال :
package com.javapro; import org.springframework.lang.Nullable;
@Repository interface UserRepository extends JpaRepository<User, Long> {
User getByEmailAddress(EmailAddress emailAddress);
Optional<User> findOptionalByEmailAddress(EmailAddress emailAddress); // Best approach } |
با تشکر از خانم ساناز اردشیری جهت تایپ نمودن محتوا.
منابع:
Oracle.com
baeldung.com
javaland
docs.spring.io
geeksforgeeks
بستن *نام و نام خانوادگی * پست الکترونیک * متن پیام |
دوره های آموزشی برنامه نویسی
انجام پروژه های برنامه نویسی
تدریس خصوصی برنامه نویسی
بیش از 7 سال از فعالیت جاواپرو میگذرد
جاواپرو دارای مجوز نشر دیجیتال از وزارت فرهنگ و ارشاد اسلامی است
جهت ارتباط مستقیم با جاواپرو در واتساپ و تلگرام :
09301904690