שאלות על ראיונות עם Java Generics (+ תשובות)

מאמר זה הוא חלק מסדרה: • שאלות ראיונות בנושא אוספי Java

• שאלות בנושא ראיונות מערכת מסוג Java

• שאלות על ראיונות במקביל ל- Java (+ תשובות)

• שאלות על ראיונות מבנה כיתת Java ו אתחול

• Java 8 שאלות ראיונות (+ תשובות)

• ניהול זיכרון בשאלות ראיון עם Java (+ תשובות)

• שאלות על ראיונות עם Java Generics (+ תשובות) (מאמר נוכחי) • שאלות על ראיונות עם בקרת זרימת Java (+ תשובות)

• שאלות על ראיונות חריגים עם Java (+ תשובות)

• שאלות ראיונות בהערות Java (+ תשובות)

• שאלות על ראיונות מסגרת האביב המובילה

1. הקדמה

במאמר זה נעבור על כמה דוגמאות לשאלות ותשובות על ראיונות גנריים של Java.

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

2. שאלות

שאלה 1. מהו פרמטר מסוג כללי?

סוּג הוא שמו של א מעמד אוֹ מִמְשָׁק. כמשתמע מהשם, פרמטר מסוג כללי הוא כאשר a סוּג יכול לשמש כפרמטר בכיתה, בשיטה או בהצהרת ממשק.

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

ממשק ציבורי צרכן {לצרוך ריק חלל (פרמטר מחרוזת)}

במקרה זה, סוג פרמטר השיטה של לִצְרוֹך() השיטה היא חוּט. זה לא פרמטרי ולא ניתן להגדרה.

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

ממשק ציבורי צרכן {לצרוך חלל ציבורי (פרמטר T)}

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

מחלקה ציבורית IntegerConsumer מיישם את הצרכן {לצרוך חלל ציבורי (פרמטר שלם)}

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

שאלה 2. מהם יתרונות השימוש בסוגים גנריים?

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

רשימת רשימה = ArrayList חדש (); list.add ("foo"); אובייקט o = list.get (0); מחרוזת foo = (מחרוזת) o;

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

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

list.add (1) Object o = list.get (0); מחרוזת foo = (מחרוזת) o;

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

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

רשימת רשימה = ArrayList חדש (); list.add ("foo"); מחרוזת o = list.get (0); // אין צוות שחקנים שלם foo = list.get (0); // שגיאת אוסף

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

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

שאלה 3. מהי מחיקת סוג?

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

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

  1. החלף סוגים גנריים באובייקטים
  2. החלף סוגים מוגבלים (עוד על אלה בשאלה מאוחרת יותר) עם המחלקה הראשונה
  3. הכנס את המקבילה לקסטות בעת אחזור אובייקטים כלליים.

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

foo public (צרכן צרכן) {Type type = consumer.getGenericTypeParameter ()}

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

שאלה 4. אם סוג כללי מושמט בעת התקנת אובייקט, האם הקוד עדיין יתאסף?

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

רשימת רשימה = ArrayList חדש ();

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

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

ש 5. במה שיטה גנרית שונה מסוג גנרי?

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

ציבורי סטטי T returnType (טיעון T) {טיעון חזרה; }

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

שאלה 6. מהי הסקת סוג?

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

Integer inferredInteger = returnType (1); מחרוזת inferredString = returnType ("מחרוזת");

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

ש 7. מהו פרמטר מסוג מוגבל?

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

כאשר אנו משתמשים בפרמטרים מוגבלים אנו מגבילים את הסוגים שיכולים לשמש כארגומנטים מסוג כללי.

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

מחלקה מופשטת ציבורית כלוב {מופשט בטל addAnimal (T חיה)}

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

כלוב חתול כלוב;

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

אובייקט כלובכלוב; // שגיאת אוסף

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

חלל ציבורי firstAnimalJump () {T animal = animals.get (0); animal.jump (); }

ש 8. האם ניתן להכריז על פרמטר מסוג מוגבל מרובה?

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

כיתה מופשטת ציבורית כלוב

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

שאלה 9. מהו סוג תו כללי?

סוג תווים כלליים מייצג אלמוני סוּג. זה מפוצץ עם סימן שאלה כדלקמן:

חלל סטטי ציבורי consumeListOfWildcardType (רשימת רשימות)

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

ש 10. מהו תו כללי מוגבל עליון?

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

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

חווה ברמה ציבורית {חיות פרטיות; חלל ציבורי addAnimals (אוסף newAnimals) {animal.addAll (newAnimals); }}

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

farm.addAnals (חתולים); // חוות שגיאות אוסף .addAnimals (כלבים); // שגיאת אוסף

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

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

addAnimals public void public (אוסף newAnimals)

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

שאלה 11. מהו תו כלול בלתי מוגבל?

תו כלול ללא גבולות הוא תו כללי ללא גבולות עליונים או תחתונים, שיכול לייצג כל סוג שהוא.

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

רשימת wildcardList = ArrayList חדש (); רשימת objectList = ArrayList חדש (); // שגיאת אוסף

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

שאלה 12. מהו תו כללי נמוך יותר?

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

ריק סטטי ציבורי addDogs (רשימת רשימה) {list.add (כלב חדש ("טום"))}

על ידי שימוש ב סוּפֶּר, נוכל לקרוא ל- addDogs ברשימת אובייקטים:

אובייקטים של ArrayList = ArrayList חדש (); addDogs (אובייקטים);

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

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

אובייקטים של ArrayList = ArrayList חדש (); addDogs (אובייקטים);

ש 13. מתי תבחר להשתמש בסוג מוגבל תחתון לעומת סוג בגבול עליון?

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

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

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

בטל סטטי ציבורי makeLotsOfNoise (רשימת בעלי חיים) {animal.forEach (בעלי חיים :: makeNoise); }

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

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

addCats בטל סטטי ציבורי (בעלי חיים ברשימה) {animal.add (חתול חדש ()); }

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

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

ש 14. האם יש מצבים שבהם מידע על סוג גנרי זמין בזמן זמן ריצה?

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

מחלקת הציבור CatCage מיישמת את Cage

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

(Class) ((ParameterizedType) getClass () .getGenericSuperclass ()). GetActualTypeArguments () [0];

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

הַבָּא » שאלות על ראיונות עם בקרת זרימת Java (+ תשובות) « ניהול זיכרון קודם בשאלות ראיון עם Java (+ תשובות)