Java אופציונלי כסוג החזרה

1. הקדמה

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

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

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

2. אופציונאלי כסוג החזרה

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

לרוב, החזרת אופציונאלי בסדר גמור:

סטטי ציבורי אופציונלי findUserByName (שם מחרוזת) {משתמש משתמש = usersByName.get (שם); אופציונלי opt = Optional.ofNullable (משתמש); להחזיר opt; }

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

שינוי ריק סטטי ציבורי User Name (String oldFirstName, String newFirstName) {findUserByFirstName (oldFirstName) .ifPresent (user -> user.setFirstName (newFirstName)); }

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

3. מתי לא לחזור אופציונאלי

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

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

נבדוק כמה ממקרי השימוש החשובים להלן.

3.1. סידור

בואו נדמיין שיש לנו ישות פשוטה:

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

זה למעשה לא יעבוד בכלל. אם ננסה לסדר את זה, היינו מקבלים NotSerializableException:

ObjectOutputStream חדש (ByteArrayOutputStream חדש ()). writeObject (גרב חדש ()); 

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

בואו נסתכל על יישום אחר של אותו חוסר התאמה סדרתי, הפעם עם JSON.

3.2. ג'סון

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

נניח שיש לנו שעועית עם מאפיין אופציונלי:

פרטי מחרוזת firstName; public אופציונלי getFirstName () {return Optional.ofNullable (firstName); } ריק ריק setFirstName (שם מחרוזת) {this.firstName = שם פרטי; }

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

{"firstName": {"present": true}} 

אבל מה שאנחנו באמת רוצים זה:

{"firstName": "Baeldung"}

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

3.3. JPA

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

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

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

הפעם, עם זאת, זוהי ישות JPA:

המחלקה הציבורית @Entity UserOptionalField מיישמת באמצעות Serializable {@ Id פרטי משתמש ארוך; פרטי שם פרטי אופציונלי; // ... גטרים וקובעים}

ובואו נמשיך וננסה להתמיד בזה:

UserOptionalField user = UserOptionalField new (); user.setUserId (1l); user.setFirstName (Optional.of ("Baeldung")); entityManager.persist (משתמש);

למרבה הצער, אנו נתקלים בשגיאה:

נגרם על ידי: javax.persistence.PersistenceException: [PersistenceUnit: com.baeldung.optionalReturnType] לא ניתן לבנות SessionFactory ב- Hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.persistenceException (EntityManagerFanagerFanagerManagerFactorBuilderImpl.persistenceException .boot.internal.EntityManagerFactoryBuilderImpl.build (EntityManagerFactoryBuilderImpl.java:941) ב- org.hibernate.jpa. התמדה.Persistence.createEntityManagerFactory (Persistence.java:54) ב- com.baeldung.optionalReturnType.PersistOptionalTypeExample. (PersistOptionalTypeExample.java:11) נגרמת על ידי: org.hibernate.MappingException: לא ניתן היה לקבוע סוג עבור: javation. טבלה: UserOptionalField, לעמודות: [org.hibernate.mapping.Column (firstName)]

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

@Column (nullable = true) פרטי מחרוזת שם פרטי; public אופציונלי getFirstName () {return Optional.ofNullable (firstName); }

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

עם זאת, עכשיו, כשאנחנו לא עולים בקנה אחד עם הגטר, המגדיר והשדה שלנו, יהיה קשה יותר למנף את ברירות המחדל של JPA ואת כלי קוד המקור IDE.

עד ל- JPA תמיכה אלגנטית של אופציונאלי צריך להקפיד על הקוד המסורתי. זה פשוט וטוב יותר:

פרטי מחרוזת firstName; // ... גטר ומסדר מסורתי

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

3.4. שפות ביטוי

הכנת DTO לחזית מציבה קשיים דומים.

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

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

אופציונלי [Baeldung] 

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

4. מסקנה

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

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

קוד המקור של הדוגמאות במדריך זה ניתן למצוא ב- GitHub.