מבוא לסוגים גנריים בג'אווה: משתנות וסותרות

סוגים

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

לדוגמה: int myInteger = 42;

הזן סוגים גנריים.

סוגים גנריים

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

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

במקום לציין objשהוא intסוג, או Stringסוג או כל סוג אחר, אתה מגדיר את Boxהמחלקה לקבל פרמטר סוג <; T>. לאחר מכן, אתה יכול nלהשתמש ב- T כדי לייצג את הסוג הגנרי הזה בחלק כלשהו בכיתה שלך.

עכשיו, היכנסו לשונות וניתנות.

משתנות וסתירות

הַגדָרָה

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

הגדרה קלה לזכירה (ובלתי פורמלית ביותר) של משתנות וסתירות היא:

  • משתנות: קבל תת-סוגים
  • סתירה: קבל סוגי-על

מערכים

בג'אווה מערכים הם משתנים , שיש להם 2 השלכות.

ראשית, מערך מסוג T[]עשוי להכיל אלמנטים מסוג וסוגי Tהמשנה שלו.

Number[] nums = new Number[5];nums[0] = new Integer(1); // Oknums[1] = new Double(2.0); // Ok

שנית, מערך מסוג S[]הוא תת-סוג של T[]אם Sהוא תת-סוג של T.

Integer[] intArr = new Integer[5];Number[] numArr = intArr; // Ok

עם זאת, חשוב לזכור כי: (1) numArrהוא התייחסות של סוג הפניה ל"אובייקט Number[]בפועל intArr"מסוג" בפועל " Integer[].

לכן, השורה הבאה תתקבל בסדר גמור, אך תייצר זמן ריצה ArrayStoreException(בגלל זיהום ערימה):

numArr[0] = 1.23; // Not ok

זה מייצר חריג בזמן ריצה, מכיוון שג'אווה יודעת בזמן הריצה כי "האובייקט האמיתי" intArrהוא למעשה מערך של Integer.

גנריות

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

ככאלה, גנריות אינן משתנות.

ArrayList intArrList = new ArrayList();ArrayList numArrList = intArrList; // Not okArrayList anotherIntArrList = intArrList; // Ok

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

אבל היכנסו לקלפי wild

כרטיסי בר, ​​משתנות וניתנות

עם תווים כלליים אפשר לגנריות לתמוך בשונות ובניגוד.

אם משנים את הדוגמה הקודמת, אנו מקבלים את זה, שעובד!

ArrayList intArrList = new ArrayList();ArrayList numArrList = intArrList; // Ok

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

לכן, בשורה 2, ? super Integerמתרגם ל"כל סוג שהוא סוג שלם או סוג העל שלו ".

אתה יכול גם להגביל את התו הכללי העליון, המגביל את הסוג הלא ידוע להיות סוג מסוים או תת-סוג שלו, באמצעות ? extends Integer.

קריאה בלבד וכתיבה בלבד

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

זכור שסוגי משתנים מקבלים תת-סוגים, כך ArrayList er> can contain any object that is either of a Number type or its subtype.

In this example, line 9 works, because we can be certain that whatever we get from the ArrayList can be upcasted to a Number type (because if it extends Number, by definition, it is a Number).

But nums.add() doesn’t work, because we cannot be sure of the “actual type” of the object. All we know is that it must be a Number or its subtypes (e.g. Integer, Double, Long, etc.).

With contravariance, the converse is true.

Line 9 works, because we can be certain that whatever the “actual type” of the object is, it must be Integer or its supertype, and thus accept an Integer object.

But line 10 doesn’t work, because we cannot be sure that we will get an Integer. For instance, nums could be referencing an ArrayList of Objects.

Applications

Therefore, since covariant types are read-only and contravariant types are write-only (loosely speaking), we can derive the following rule of thumb: “Producer extends, consumer super”.

A producer-like object that produces objects of type T can be of type parameter T>, while a consumer-like object that consumes objects oftype T can be of type parameter super T>.