אתגרים בג'אווה 8

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

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

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

2. Java 8 Stream and Pool Thread

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

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

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

למרבה המזל, יש פיתרון לכך:

ForkJoinPool forkJoinPool = ForkJoinPool חדש (2); forkJoinPool.submit (() -> / * צינור זרם מקביל כלשהו * /) .get ();

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

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

3. הפחתת ניתורי ניפוי

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

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

intic public static int getLength (String input) {if (StringUtils.isEmpty (input) {throw new IllegalArgumentException ();} return input.length ();} אורכי רשימה = ArrayList חדש (); עבור (שם מחרוזת: Arrays.asList ( args)) {lengths.add (getLength (name));}

זהו קוד Java חיוני סטנדרטי שמסביר את עצמו.

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

ב- LmbdaMain.getLength (LmbdaMain.java:19) ב- LmbdaMain.main (LmbdaMain.java:34)

עכשיו, בואו נכתוב מחדש את אותו קוד באמצעות Stream API ונראה מה קורה כאשר ריק חוּט עובר:

אורכי זרם = names.stream () .map (שם -> getLength (שם));

מחסנית השיחות תיראה כך:

ב- LmbdaMain.getLength (LmbdaMain.java:19) ב- LmbdaMain.lambda $ 0 (LmbdaMain.java:37) ב- LmbdaMain $$ Lambda $ 1 / 821270929. החל (מקור לא ידוע) ב- java.util.stream. ReferencePipeline $ 3 $ 1. קבלה ( ReferencePipeline.java:193) ב- java.util.Spliterators $ ArraySpliterator.forEachRemaining (Spliterators.java:948) ב- java.util.stream.AbstractPipeline.copyInto (AbstractPipeline.java:512) ב- java.util.stream.AbstractPipeline.wrapondCopy (AbstractPipeline.java:502) ב- java.util.stream.ReduceOps $ ReduceOp.evaluateSequential (ReduceOps.java:708) ב- java.util.stream.AbstractPipeline.evaluate (AbstractPipeline.java:234) ב- java.util.stream. LongPipeline.reduce (LongPipeline.java:438) ב- java.util.stream.LongPipeline.sum (LongPipeline.java:396) ב- java.util.stream.ReferencePipeline.count (ReferencePipeline.java:526) ב- LmbdaMain.main (LmbdaMain .java: 39)

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

4. שיטות חזרה ריק אוֹ אופציונאלי

אופציונאלי הוצג בג'אווה 8 כדי לספק דרך בטוחה לביטוי אופציונליות.

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

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

5. יותר מדי ממשקים פונקציונליים

בתוך ה java.util.function חבילה, יש לנו אוסף של סוגי יעד לביטויים למבדה. אנו יכולים להבחין ולקבץ אותם כך:

  • צרכן - מייצג פעולה שלוקחת כמה טיעונים ולא מחזירה שום תוצאה
  • פוּנקצִיָה - מייצג פונקציה שלוקחת כמה טיעונים ומייצרת תוצאה
  • מַפעִיל - מייצג פעולה בכמה ארגומנטים מסוג ומחזיר תוצאה מאותו סוג כמו האופרנדים
  • לְבַסֵס - מייצג פרדיקט (בוליאניפונקציה מוערכת) של כמה טיעונים
  • ספק - מייצג ספק שאינו לוקח ויכוחים ומחזיר תוצאות

בנוסף, יש לנו סוגים נוספים לעבודה עם פרימיטיבים:

  • IntConsumer
  • IntFunction
  • IntPredicate
  • ספקית Int
  • IntToDoubleFunction
  • IntToLongFunction
  • ... ואותן חלופות עבור כמהה ו זוגות

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

  • BiConsumer
  • BiPredicate
  • BinaryOperator
  • BiFunction

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

6. חריגים מסומנים וביטויים למבדה

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

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

סטטי ריק ריק writeToFile (מספר שלם שלם) זורק IOException {// לוגיקה לכתוב לקובץ אשר זורק IOException}
רשימת מספרים שלמים = Arrays.asList (3, 9, 7, 0, 10, 20); integers.forEach (i -> writeToFile (i));

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

רשימת מספרים שלמים = Arrays.asList (3, 9, 7, 0, 10, 20); integers.forEach (i -> {try {writeToFile (i);} לתפוס (IOException e) {לזרוק RuntimeException (e);}});

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

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

ממשק ציבורי @FunctionalInterface ThrowingConsumer {void accept (T t) זורק E; }
סטטי צרכני לזרוק ConsumerWrapper (ThrowingConsumer throwingConsumer) {החזר i -> {נסה {throwingConsumer.accept (i); } לתפוס (Exception ex) {לזרוק RuntimeException חדש (ex); }}; }

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

לבסוף, לפיתרון מעמיק ולהסבר על הבעיה, נוכל לחקור את הצלילה העמוקה הבאה: חריגים ב- Java 8 Lambda Expressions.

8. סיכום

בכתב מהיר זה דנו בכמה מהחסרונות של Java 8.

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


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