מדריך ל- Java 8 אופציונלי

1. סקירה כללית

במדריך זה אנו הולכים להראות את אופציונאלי מחלקה שהוצגה ב- Java 8.

מטרת הכיתה היא לספק פתרון ברמת סוג לייצוג ערכים אופציונליים במקום ריק הפניות.

כדי לקבל הבנה מעמיקה יותר מדוע עלינו לדאוג ל אופציונאלי בכיתה, התבונן במאמר הרשמי של אורקל.

2. יצירה אופציונאלי חפצים

ישנן מספר דרכים ליצור אופציונאלי חפצים.

כדי ליצור ריק אופציונאלי אובייקט, אנחנו פשוט צריכים להשתמש בו ריק() שיטה סטטית:

@ מבחן ציבורי בטל whenCreatesEmptyOptional_thenCorrect () {Optional ריק = Optional.empty (); assertFalse (empty.isPresent ()); }

שים לב שהשתמשנו ב- isPresent () שיטה לבדוק אם יש ערך בתוך ה- אופציונאלי לְהִתְנַגֵד. ערך קיים רק אם יצרנו אופציונאלי עם לאריק ערך. נסתכל על isPresent () השיטה בסעיף הבא.

אנחנו יכולים גם ליצור אופציונאלי חפץ בשיטה הסטטית שֶׁל():

@ מבחן הריק ציבורי givenNonNull_whenCreatesNonNullable_thenCorrect () {שם מחרוזת = "baeldung"; אופציונלי opt = Optional.of (שם); assertTrue (opt.isPresent ()); }

עם זאת, הטיעון הועבר ל שֶׁל() השיטה לא יכולה להיות ריק. אחרת נקבל NullPointerException:

@Test (צפוי = NullPointerException.class) חלל ציבורי שניתן Null_whenThrowsErrorOnCreate_thenCorrect () {שם מחרוזת = null; Optional.of (שם); }

אבל למקרה שאנחנו מצפים לכמה ריק אנו יכולים להשתמש בערכים ofNullable () שיטה:

@ מבחן הריק ציבורי שניתן NonNull_whenCreatesNullable_thenCorrect () {שם מחרוזת = "baeldung"; אופציונלי opt = Optional.ofNullable (שם); assertTrue (opt.isPresent ()); }

על ידי כך, אם נעבור ב ריק התייחסות, זה לא זורק חריג אלא מחזיר ריק אופציונאלי לְהִתְנַגֵד:

@ מבחן פומבי בטל givenNull_whenCreatesNullable_thenCorrect () {שם מחרוזת = null; אופציונלי opt = Optional.ofNullable (שם); assertFalse (opt.isPresent ()); }

3. בדיקת נוכחות ערך: isPresent () ו זה ריק()

כשיש לנו אופציונאלי אובייקט שהוחזר משיטה או שנוצר על ידינו, נוכל לבדוק אם יש בו ערך או לא עם ה- isPresent () שיטה:

@ מבחן פומבי בטל שניתןOptional_whenIsPresentWorks_thenCorrect () {אופציונלי opt = Optional.of ("Baeldung"); assertTrue (opt.isPresent ()); opt = Optional.ofNullable (null); assertFalse (opt.isPresent ()); }

שיטה זו חוזרת נָכוֹן אם הערך העטוף אינו ריק.

כמו כן, נכון ל- Java 11, אנו יכולים לעשות את ההפך עם ה- זה ריק שיטה:

@Test הציבור בטל givenAnEmptyOptional_thenIsEmptyBehavesAsExpected () {אופציונלי opt = Optional.of ("Baeldung"); assertFalse (opt.isEmpty ()); opt = Optional.ofNullable (null); assertTrue (opt.isEmpty ()); }

4. פעולה מותנית עם אם נמצא()

ה אם נמצא() השיטה מאפשרת לנו להפעיל קוד כלשהו על הערך העטוף אם הוא נמצא שאינוריק. לפני אופציונאליהיינו עושים:

אם (שם! = null) {System.out.println (name.length ()); }

קוד זה בודק אם משתנה השם הוא ריק או לא לפני שתמשיך לבצע קוד כלשהו עליו. גישה זו ארוכה, וזו לא הבעיה היחידה - היא גם נוטה לטעויות.

ואכן, מה שמבטיח שלאחר הדפסת המשתנה הזה, לא נשתמש בו שוב ואז לשכוח לבצע את בדיקת האפס?

זה יכול לגרום ל- NullPointerException בזמן ריצה אם ערך null מוצא את דרכו לקוד זה. כאשר תוכנית נכשלת בגלל בעיות קלט, זה לעתים קרובות תוצאה של פרקטיקות תכנות לקויות.

אופציונאלי גורם לנו להתמודד עם ערכים בטלים באופן מפורש כדרך לאכוף שיטות תכנות טובות.

בואו נסתכל כיצד ניתן לשחזר את הקוד הנ"ל ב- Java 8.

בסגנון תכנות פונקציונלי אופייני, אנו יכולים לבצע פעולה על אובייקט הקיים בפועל:

@ מבחן הריק פומבי בהתחשב ב- Optional_whenIfPresentWorks_thenCorrect () {Optional opt = Optional.of ("baeldung"); opt.ifPresent (שם -> System.out.println (name.length ())); }

בדוגמה שלעיל אנו משתמשים רק בשתי שורות קוד כדי להחליף את החמש שעבדו בדוגמה הראשונה: שורה אחת כדי לעטוף את האובייקט לתוך אופציונאלי האובייקט והבא לבצע אימות מרומז וכן לבצע את הקוד.

5. ערך ברירת מחדל עם אחרת()

ה אחרת() השיטה משמשת לאחזור הערך העטוף בתוך אופציונאלי למשל. נדרש פרמטר אחד, המשמש כערך ברירת מחדל. ה אחרת() השיטה מחזירה את הערך העטוף אם הוא קיים, ואת הטיעון שלו אחרת:

@ מבחן ציבורי בטל כאשר OrElseWorks_thenCorrect () {String nullName = null; שם מחרוזת = Optional.ofNullable (nullName) .orElse ("ג'ון"); assertEquals ("ג'ון", שם); }

6. ערך ברירת מחדל עם orElseGet ()

ה orElseGet () שיטה דומה ל- אחרת(). עם זאת, במקום לקחת ערך להחזרה אם אופציונאלי הערך אינו קיים, נדרש ממשק פונקציונלי של הספק, שמופעל ומחזיר את ערך ההפעלה:

@Test ציבורי בטל כאשר OrElseGetWorks_thenCorrect () {String nullName = null; שם מחרוזת = Optional.ofNullable (nullName) .orElseGet (() -> "john"); assertEquals ("ג'ון", שם); }

7. ההבדל בין אחרת ו orElseGet ()

להרבה מתכנתים שהם חדשים ב אופציונאלי או Java 8, ההבדל בין אחרת() ו orElseGet () לא ברור. למען האמת, שתי השיטות הללו נותנות את הרושם שהן חופפות זו את זו בפונקציונליות.

עם זאת, יש הבדל עדין אך חשוב מאוד בין השניים שיכול להשפיע בצורה דרסטית על ביצועי הקוד שלנו אם לא מובן היטב.

בואו ניצור שיטה שנקראת getMyDefault () בשיעור הבדיקה, שאינו לוקח ארגומנטים ומחזיר ערך ברירת מחדל:

מחרוזת ציבורית getMyDefault () {System.out.println ("קבלת ערך ברירת מחדל"); להחזיר "ערך ברירת מחדל"; }

בואו נראה שתי בדיקות ונצפה בתופעות הלוואי שלהן כדי לקבוע את שתיהן היכן אחרת() ו orElseGet () חופפים והיכן הם נבדלים:

@ מבחן ציבורי בטל כאשר OrElseGetAndOrElseOverlap_thenCorrect () {טקסט מחרוזת = null; מחרוזת defaultText = Optional.ofNullable (טקסט) .orElseGet (זה :: getMyDefault); assertEquals ("ערך ברירת מחדל", defaultText); defaultText = Optional.ofNullable (טקסט). orElse (getMyDefault ()); assertEquals ("ערך ברירת מחדל", defaultText); }

בדוגמה שלעיל, אנו עוטפים טקסט null בתוך אופציונאלי להתנגד ולנסות להשיג את הערך העטוף באמצעות כל אחת משתי הגישות.

תופעת הלוואי היא:

מקבל ערך ברירת מחדל ... מקבל ערך ברירת מחדל ...

ה getMyDefault () שיטה נקראת בכל מקרה. כך קורה כאשר הערך העטוף אינו קיים, אז שניהם אחרת() ו orElseGet () לעבוד בדיוק באותה צורה.

עכשיו בואו נבצע בדיקה נוספת שבה הערך קיים, ובאופן אידיאלי, אפילו לא צריך ליצור את ערך ברירת המחדל:

@ מבחן ציבורי בטל כאשר OrElseGetAndOrElseDiffer_thenCorrect () {String text = "טקסט קיים"; System.out.println ("שימוש ב- orElseGet:"); מחרוזת defaultText = Optional.ofNullable (טקסט) .orElseGet (זה :: getMyDefault); assertEquals ("טקסט קיים", defaultText); System.out.println ("באמצעות orElse:"); defaultText = Optional.ofNullable (טקסט). orElse (getMyDefault ()); assertEquals ("טקסט קיים", defaultText); }

בדוגמה שלעיל, אנחנו כבר לא עוטפים א ריק ערך, ושאר הקוד נשאר זהה.

עכשיו בואו נסתכל על תופעת הלוואי של הפעלת קוד זה:

שימוש ב- orElseGet: שימוש ב- orElse: קבלת ערך ברירת מחדל ...

שימו לב שבשימוש orElseGet () כדי לאחזר את הערך העטוף, את getMyDefault () אפילו השיטה אינה מופעלת מכיוון שהערך הכלול קיים.

עם זאת, בעת השימוש אחרת(), בין אם הערך העטוף קיים ובין אם לאו, נוצר אובייקט ברירת המחדל. אז במקרה זה, יצרנו זה עתה אובייקט מיותר שלא נעשה בו שימוש לעולם.

בדוגמה פשוטה זו, אין עלות משמעותית ליצירת אובייקט ברירת מחדל, מכיוון שה- JVM יודע להתמודד עם כזה. עם זאת, כאשר שיטה כגון getMyDefault () צריך לבצע שיחת שירות אינטרנט או אפילו לבצע שאילתות על בסיס נתונים, העלות הופכת מאוד ברורה.

8. חריגים עם orElseThrow ()

ה orElseThrow () השיטה נובעת מ אחרת() ו orElseGet () ומוסיף גישה חדשה לטיפול בערך נעדר.

במקום להחזיר ערך ברירת מחדל כאשר הערך העטוף אינו קיים, הוא יוצא חריג:

@Test (צפוי = IllegalArgumentException.class) בטל ציבורי כאשרOrElseThrowWorks_thenCorrect () {String nullName = null; שם מחרוזת = Optional.ofNullable (nullName) .orElseThrow (IllegalArgumentException :: new); }

הפניות לשיטות ב- Java 8 שימושיות כאן, כדי להעביר את בונה החריגים.

Java 10 הציגה גרסה פשוטה ללא ארגון של orElseThrow () שיטה. במקרה של ריק אופציונאלי זה זורק א NoSuchElelementException:

@Test (צפוי = NoSuchElementException.class) בטל בציבור כאשר NoArgOrElseThrowWorks_thenCorrect () {מחרוזת nullName = null; שם מחרוזת = Optional.ofNullable (nullName) .orElseThrow (); }

9. החזרת ערך עם לקבל()

הגישה הסופית לאחזור הערך העטוף היא לקבל() שיטה:

@ מבחן חלל ציבורי בהתחשב ב- Optional_whenGetsValue_thenCorrect () {Optional opt = Optional.of ("baeldung"); שם מחרוזת = opt.get (); assertEquals ("baeldung", שם); }

עם זאת, בניגוד לשלוש הגישות הקודמות, לקבל() יכול להחזיר ערך רק אם האובייקט העטוף אינו ריק; אחרת זה לא יוצא חריג של אלמנט כזה:

@Test (צפוי = NoSuchElementException.class) חלל ציבורי givenOptionalWithNull_whenGetThrowsException_thenCorrect () {אופציונלי opt = Optional.ofNullable (null); שם מחרוזת = opt.get (); }

זהו הפגם העיקרי של לקבל() שיטה. באופן אידיאלי, אופציונאלי אמור לעזור לנו להימנע מחריגים בלתי צפויים כאלה. לכן גישה זו פועלת כנגד היעדים של אופציונאלי וכנראה שיופסק במהדורה עתידית.

לכן, מומלץ להשתמש בגרסאות האחרות המאפשרות לנו להתכונן ולטפל במפורש ב ריק מקרה.

10. חזרה מותנית עם לְסַנֵן()

אנו יכולים לבצע בדיקה מוטבעת על הערך העטוף שלנו עם ה- לְסַנֵן שיטה. זה לוקח פרדיקט כטענה ומחזיר אופציונאלי לְהִתְנַגֵד. אם הערך העטוף עובר את הבדיקה על ידי הקביעה, אז ה- אופציונאלי מוחזר כמו שהוא.

עם זאת, אם הפרדיקט חוזר שֶׁקֶרואז הוא יחזיר ריק אופציונאלי:

@Test הציבור בטל כאשרOptionalFilterWorks_thenCorrect () {שנת שלמה = 2016; שנה אופציונלית אופציונלית = Optional.of (שנה); בוליאני is2016 = yearOptional.filter (y -> y == 2016) .isPresent (); assertTrue (is2016); בוליאני is2017 = yearOptional.filter (y -> y == 2017) .isPresent (); assertFalse (is2017); }

ה לְסַנֵן בדרך כלל משתמשים בשיטה זו לדחיית ערכים עטופים על בסיס כלל מוגדר מראש. אנו יכולים להשתמש בו כדי לדחות פורמט שגוי של דוא"ל או סיסמה שאינה חזקה מספיק.

בואו נסתכל על דוגמא משמעותית אחרת. נגיד שאנחנו רוצים לקנות מודם, ואנחנו דואגים רק למחירו.

אנו מקבלים התראות דחיפה על מחירי מודמים מאתר מסוים ושומרים אותם באובייקטים:

מודם ברמה ציבורית {מחיר כפול פרטי; מודם ציבורי (מחיר כפול) {this.price = מחיר; } // גטרים וקובעים סטנדרטיים}

לאחר מכן אנו מזינים את האובייקטים האלה לקוד כלשהו שמטרתו היחידה לבדוק אם מחיר המודם נמצא בטווח התקציב שלנו.

בואו עכשיו נסתכל על הקוד בלי אופציונאלי:

price בוליאני ציבוריIsInRange1 (מודם מודם) {בוליאני isInRange = false; if (modem! = null && modem.getPrice ()! = null && (modem.getPrice ()> = 10 && modem.getPrice () <= 15)) {isInRange = true; } החזרה isInRange; }

שים לב כמה קוד עלינו לכתוב כדי להשיג זאת, במיוחד ב אם מַצָב. החלק היחיד של אם התנאי הקריטי ליישום הוא בדיקת טווח המחירים האחרונה; שאר הצ'קים הם הגנתיים:

@Test הציבור בטל כאשרFiltersWithoutOptional_thenCorrect () {assertTrue (priceIsInRange1 (מודם חדש (10.0))); assertFalse (priceIsInRange1 (מודם חדש (9.9))); assertFalse (priceIsInRange1 (מודם חדש (null))); assertFalse (priceIsInRange1 (מודם חדש (15.5))); assertFalse (priceIsInRange1 (null)); }

מלבד זאת, אפשר לשכוח מהמחאות האפס במשך יום ארוך מבלי לקבל שגיאות זמן הידור.

עכשיו בואו נסתכל על גרסה עם מסנן # אופציונלי:

price boolean publicIsInRange2 (modem2 modem) {return Optional.ofNullable (modem2) .map (Modem :: getPrice) .filter (p -> p> = 10) .filter (p -> p <= 15) .isPresent (); }

ה מַפָּה שיחה משמשת פשוט כדי להפוך ערך לערך אחר. זכור כי פעולה זו אינה משנה את הערך המקורי.

במקרה שלנו, אנו משיגים אובייקט מחיר מה- דֶגֶם מעמד. אנו נסתכל על מַפָּה() השיטה בפירוט בסעיף הבא.

קודם כל, אם א ריק האובייקט מועבר לשיטה זו, אנו לא מצפים לבעיה כלשהי.

שנית, ההיגיון היחיד שאנו כותבים בגופו הוא בדיוק מה שמתאר שם השיטה - בדיקת טווח מחירים. אופציונאלי דואג לשאר:

@Test הציבור בטל כאשרFiltersWithOptional_thenCorrect () {assertTrue (priceIsInRange2 (מודם חדש (10.0))); assertFalse (priceIsInRange2 (מודם חדש (9.9))); assertFalse (priceIsInRange2 (מודם חדש (null))); assertFalse (priceIsInRange2 (מודם חדש (15.5))); assertFalse (priceIsInRange2 (null)); }

הגישה הקודמת מבטיחה לבדוק את טווח המחירים, אך עליה לעשות יותר מכך כדי להגן מפני השבריריות הטבועה בה. לכן, אנו יכולים להשתמש ב- לְסַנֵן שיטה להחלפה מיותרת אם הצהרות ודוחים ערכים לא רצויים.

11. שינוי ערך עם מַפָּה()

בחלק הקודם בדקנו כיצד לדחות או לקבל ערך המבוסס על פילטר.

אנו יכולים להשתמש בתחביר דומה כדי לשנות את ה- אופציונאלי ערך עם מַפָּה() שיטה:

@Test הציבור בטל שניתןOptional_whenMapWorks_thenCorrect () {רשימה companyNames = Arrays.asList ("paypal", "אורקל", "", "מיקרוסופט", "", "תפוח"); אופציונאלי listOptional = Optional.of (companyNames); int size = listOptional .map (List :: size) .orElse (0); assertEquals (6, גודל); }

בדוגמה זו, אנו עוטפים רשימה של מחרוזות בתוך אופציונאלי להתנגד ולהשתמש בו מַפָּה שיטה לביצוע פעולה ברשימה הכלולה. הפעולה שאנו מבצעים היא לאחזר את גודל הרשימה.

ה מַפָּה השיטה מחזירה את תוצאת החישוב העטופה בפנים אופציונאלי. לאחר מכן עלינו להתקשר לשיטה מתאימה בחזרה אופציונאלי כדי לאחזר את ערכו.

שימו לב שה- לְסַנֵן השיטה פשוט מבצעת בדיקת ערך ומחזירה a בוליאני. ה מַפָּה אולם השיטה לוקחת את הערך הקיים, מבצעת חישוב באמצעות ערך זה ומחזירה את תוצאת החישוב העטופה ב- אופציונאלי לְהִתְנַגֵד:

@ מבחן פומבי בטל שניתןOptional_whenMapWorks_thenCorrect2 () {שם מחרוזת = "baeldung"; שם אופציונלי אופציונלי = Optional.of (שם); int len ​​= nameOptional .map (String :: length) .orElse (0); assertEquals (8, len); }

אנחנו יכולים לשרשר מַפָּה ו לְסַנֵן ביחד לעשות משהו חזק יותר.

נניח שאנחנו רוצים לבדוק את נכונות קלט הסיסמה על ידי המשתמש. אנו יכולים לנקות את הסיסמה באמצעות מַפָּה טרנספורמציה ובדוק את נכונותה באמצעות לְסַנֵן:

@ מבחן חלל ציבורי שניתןOptional_whenMapWorksWithFilter_thenCorrect () {סיסמת מחרוזת = "סיסמה"; אופציונלי passOpt = Optional.of (סיסמה); correctPassword בוליאני = passOpt.filter (לעבור -> pass.equals ("סיסמה")). isPresent (); assertFalse (correctPassword); correctPassword = passOpt .map (String :: trim) .filter (pass -> pass.equals ("password")) .isPresent (); assertTrue (correctPassword); }

כפי שאנו רואים, מבלי לנקות תחילה את הקלט, הוא יסונן - אך המשתמשים עשויים לקחת כמובן מאליו שכולם רווחים מובילים ונגררים מהווים קלט. לכן, אנו הופכים סיסמא מלוכלכת לסיסמה נקייה עם מַפָּה לפני סינון אלה שגויים.

12. שינוי ערך עם flatMap ()

בדיוק כמו ה- מַפָּה() שיטה, יש לנו גם את flatMap () שיטה כחלופה לשינוי ערכים. ההבדל הוא בכך מַפָּה הופך ערכים רק כאשר הם נפרשים ואילו flatMap לוקח ערך עטוף ופורק אותו לפני שהוא הופך אותו.

בעבר, יצרנו פשוט חוּט ו מספר שלם חפצים לעטיפה אופציונאלי למשל. עם זאת, לעיתים קרובות אנו נקבל אובייקטים אלה ממקור של אובייקט מורכב.

כדי לקבל תמונה ברורה יותר של ההבדל, בואו נסתכל על a אדם אובייקט שלוקח את פרטי האדם כמו שם, גיל וסיסמה:

אדם בכיתה ציבורית {פרטי שם מחרוזת; גיל פרטי פרטי; סיסמת מחרוזת פרטית; public אופציונלי getName () {return Optional.ofNullable (שם); } ציבורי אופציונלי getAge () {return Optional.ofNullable (גיל); } ציבורי אופציונלי getPassword () {return Optional.ofNullable (סיסמה); } // בונים וקובעים רגילים}

בדרך כלל היינו יוצרים אובייקט כזה ועוטפים אותו בתוך אופציונאלי חפץ בדיוק כמו שעשינו עם מחרוזת.

לחלופין, ניתן להחזיר אותנו בשיחת שיטה אחרת:

אדם אדם = אדם חדש ("ג'ון", 26); אדם אופציונלי אופציונלי = Optional.of (אדם);

שימו לב עכשיו שכשאנחנו עוטפים א אדם אובייקט, הוא יכיל קינון אופציונאלי מקרים:

@ מבט פומבי בטל שניתן אופציונלי_לפני FlatMapWorks_thenCorrect2 () {אדם אדם = אדם חדש ("ג'ון", 26); אדם אופציונלי אופציונלי = Optional.of (אדם); אופציונאלי nameOptionalWrapper = personOptional.map (אדם :: getName); שם אופציונלי Optional = nameOptionalWrapper.orElseThrow (IllegalArgumentException :: new); מחרוזת name1 = nameOptional.orElse (""); assertEquals ("john", name1); שם מחרוזת = personOptional .flatMap (אדם :: getName) .orElse (""); assertEquals ("ג'ון", שם); }

הנה, אנו מנסים לאחזר את תכונת השם של ה- אדם מתנגד לבצע קביעה.

שים לב כיצד אנו משיגים זאת באמצעות מַפָּה() בשיטה השלישית, ואז שים לב איך אנחנו עושים את אותו הדבר עם flatMap () שיטה אחר כך.

ה אדם :: getName התייחסות לשיטה דומה ל- מחרוזת :: לקצץ שיחה שהיתה לנו בסעיף הקודם לניקוי סיסמה.

ההבדל היחיד הוא בכך getName () מחזירה אופציונאלי ולא מחרוזת כפי שעשתה ה- מְטוּפָּח() מבצע. זאת, יחד עם העובדה כי א מַפָּה טרנספורמציה עוטפת את התוצאה ב- אופציונאלי אובייקט, מוביל לקינון אופציונאלי.

תוך כדי שימוש מַפָּה() לכן, עלינו להוסיף שיחה נוספת בכדי לאחזר את הערך לפני השימוש בערך המומר. בדרך זו, ה אופציונאלי העטיפה תוסר. פעולה זו מתבצעת באופן מרומז בעת השימוש flatMap.

13. שרשור אופציונאליs ב- Java 8

לפעמים, ייתכן שנצטרך לקבל את הראשון שאינו ריק אופציונאלי אובייקט ממספר אופציונאליס. במקרים כאלה, זה יהיה מאוד נוח להשתמש בשיטה כמו orElseOptional (). למרבה הצער, פעולה כזו אינה נתמכת ישירות ב- Java 8.

ראשית נציג מספר שיטות בהן נשתמש בכל חלק זה:

פרטי אופציונלי getEmpty () {return Optional.empty (); } פרטי אופציונלי getHello () {return Optional.of ("שלום"); } getBye אופציונלי פרטי () {return Optional.of ("ביי"); } פרטי אופציונלי createOptional (קלט מחרוזת) {if (input == null || "" .equals (input) || "ריק" .equals (input)) {return Optional.empty (); } להחזיר Optional.of (קלט); }

על מנת לשרשר כמה אופציונאלי אובייקטים ולקבל את הראשון שאינו ריק ב- Java 8, נוכל להשתמש ב- זרם ממשק API:

@Test public void givenThreeOptionals_whenChaining_thenFirstNonEmptyIsReturned () {Optional found = Stream.of (getEmpty (), getHello (), getBye ()) .filter (Optional :: isPresent) .map (Optional :: get) .findFirst (); assertEquals (getHello (), נמצא); }

החיסרון בגישה זו הוא שכולנו לקבל שיטות מבוצעות תמיד, ללא קשר למקום שאינו ריק אופציונאלי מופיע ב- זרם.

אם אנו רוצים להעריך בעצלתיים את השיטות שעברו זרם של(), עלינו להשתמש בהתייחסות לשיטה וב- ספק מִמְשָׁק:

@Test הציבור בטל givenThreeOptionals_whenChaining_thenFirstNonEmptyIsReturnedAndRestNotEvaluated () {אופציונלי נמצא = זרם.<>> של (זה :: getEmpty, זה :: getHello, זה :: getBye) .map (ספק :: get) .filter (אופציונלי :: isPresent) .map (אופציונלי :: get) .findFirst (); assertEquals (getHello (), נמצא); }

במקרה שנצטרך להשתמש בשיטות שנוקטות ויכוחים, עלינו לנקוט בביטויים למבדה:

@Test הציבור בטל givenTwoOptionalsReturnedByOneArgMethod_whenChaining_thenFirstNonEmptyIsReturned () {אופציונלי נמצא = זרם.<>> של (() -> createOptional ("ריק"), () -> createOptional ("שלום")) .map (ספק :: get) .filter (אופציונלי :: isPresent) .map (אופציונלי :: get). findFirst (); assertEquals (createOptional ("שלום"), נמצא); }

לעיתים קרובות נרצה להחזיר ערך ברירת מחדל למקרה שכולם משורשרים אופציונאליs ריקים. אנו יכולים לעשות זאת רק על ידי הוספת שיחה אל אחרת() אוֹ orElseGet ():

@Test הציבור בטל givenTwoEmptyOptionals_whenChaining_thenDefaultIsReturned () {מחרוזת נמצא = זרם.<>> של (() -> createOptional ("ריק"), () -> createOptional ("ריק")) .map (ספק :: קבל) .filter (אופציונלי :: isPresent) .map (אופציונלי :: get). findFirst () .orElseGet (() -> "ברירת מחדל"); assertEquals ("ברירת מחדל", נמצא); }

14. JDK 9 אופציונאלי ממשק API

שחרורו של Java 9 הוסיף עוד שיטות חדשות ל- אופציונאלי ממשק API:

  • אוֹ() שיטה לספק ספק היוצר אלטרנטיבה אופציונאלי
  • ifPresentOrElse () שיטה המאפשרת ביצוע פעולה אם אופציונאלי קיים או פעולה אחרת אם לא
  • זרם() שיטה להמרת אופציונאלי אל א זרם

לפניכם המאמר השלם לקריאה נוספת.

15. שימוש לרעה ב אופציונאליס

לבסוף, בואו נראה דרך מפתה, מסוכנת ככל שתהיה אופציונאליs: עובר אופציונאלי פרמטר לשיטה.

תאר לעצמך שיש לנו רשימה של אדם ואנחנו רוצים שיטה לחפש ברשימה זו אנשים עם שם נתון. כמו כן, אנו רוצים שהשיטה הזו תתאים לערכים לפחות לגיל מסוים, אם היא מוגדרת.

עם פרמטר זה להיות אופציונלי, אנו מגיעים עם שיטה זו:

חיפוש סטטי ציבורי ציבורי (רשימת אנשים, שם מחרוזת, גיל אופציונלי) {// בדיקות אפסות של אנשים ושמות אנשים מחזירים. stream () .filter (p -> p.getName (). שווה (שם)). filter (p -> p.getAge (). get ()> = age.orElse (0)) .collect (Collectors.toList ()); }

ואז אנו משחררים את השיטה שלנו, ומפתח אחר מנסה להשתמש בה:

someObject.search (אנשים, "פיטר", null);

כעת היזם מבצע את הקוד שלו ומקבל NullPointerException.הנה, עלינו לבטל את הפרמטר האופציונלי שלנו, המביס את מטרתנו הראשונית ברצון להימנע ממצב מסוג זה.

להלן כמה אפשרויות שיכולנו לעשות בכדי לטפל בזה טוב יותר:

חיפוש סטטי ציבורי ציבורי (רשימת אנשים, שם מחרוזת, גיל שלם) {// בדיקות אפסות עבור אנשים ושם סופי מספר שלם ageFilter = age! = null? גיל: 0; להחזיר people.stream () .filter (p -> p.getName (). שווה (שם)). filter (p -> p.getAge (). get ()> = ageFilter) .collect (Collectors.toList () ); }

שם, הפרמטר עדיין אופציונלי, אך אנו מטפלים בו בבדיקה אחת בלבד.

אפשרות אחרת הייתה ליצור שתי שיטות עמוסות יתר:

חיפוש רשימות סטטי ציבורי (רשימת אנשים, שם מחרוזת) {החזר doSearch (אנשים, שם, 0); } חיפוש רשימות סטטי ציבורי (רשימת אנשים, שם מחרוזת, גיל int) {return doSearch (אנשים, שם, גיל); } רשימה סטטית פרטית doSearch (רשימת אנשים, שם מחרוזת, גיל int) {// בדיקות אפסות של אנשים ושמות מחזירים people.stream () .filter (p -> p.getName (). שווה (שם)) .filter ( p -> p.getAge (). get (). intValue ()> = age) .collect (Collectors.toList ()); }

בדרך זו אנו מציעים ממשק API ברור עם שתי שיטות שעושות דברים שונים (אם כי הן חולקות את היישום).

אז יש פתרונות להימנע משימוש אופציונאליs כפרמטרים של שיטה. הכוונה של Java בעת שחרורו אופציונאלי היה להשתמש בו כסוג החזרה, ובכך מציין ששיטה יכולה להחזיר ערך ריק. למען האמת, הנוהג להשתמש אופציונאלי כפרמטר של שיטה אף מתייאש על ידי פקחי קוד מסוימים.

16. אופציונאלי וסידור

כפי שנדון לעיל, אופציונאלי נועד לשמש כסוג החזרה. לא מומלץ לנסות להשתמש בו כסוג שדה.

בנוסף, באמצעות אופציונאלי בכיתה מסווגת יביא ל- NotSerializableException. המאמר שלנו ג'אווה אופציונאלי כמו סוג החזרה מטפל בהמשך בבעיות בסידור.

ובשימוש אופציונאלי עם ג'קסון אנו מסבירים מה קורה מתי אופציונאלי השדות מסודרים, יחד עם כמה דרכים לעקיפת הבעיה כדי להשיג את התוצאות הרצויות.

17. מסקנה

במאמר זה סקרנו את רוב התכונות החשובות של Java 8 אופציונאלי מעמד.

בדקנו בקצרה כמה סיבות מדוע נבחר להשתמש אופציונאלי במקום בדיקת אפס מפורשת ואימות קלט.

למדנו גם כיצד להשיג את הערך של אופציונאלי, או ברירת מחדל אם היא ריקה, עם לקבל(), אחרת() ו orElseGet () שיטות (וראו את ההבדל החשוב בין שני האחרונים).

ואז ראינו כיצד לשנות או לסנן את שלנו אופציונאליעם מפה (), flatMap () ו לְסַנֵן(). דנו איזה שוטף ממשק APIאופציונאלי מציע, מכיוון שהוא מאפשר לנו לשרשר את השיטות השונות בקלות.

לבסוף, ראינו מדוע להשתמש אופציונאליכפרמטרים של שיטה זה רעיון רע וכיצד להימנע ממנו.

קוד המקור לכל הדוגמאות במאמר זמין באתר GitHub.


$config[zx-auto] not found$config[zx-overlay] not found