מבוא לגנריות
חלק מהיחידה תכנות מונחה עצמים במסלול ה-Java של Coddy. שיעור 53 מתוך 87.
דמיינו שעליכם ליצור מחלקה ששומרת ערך ומאחזרת אותו בהמשך. ללא טיפוסים גנריים, תצטרכו לכתוב מחלקות נפרדות לכל טיפוס: אחת עבור Integer, אחת עבור String, אחת עבור Double, וכן הלאה. הדבר מוביל לשכפול קוד ולקשיי תחזוקה.
Generics פותרים את הבעיה הזאת בכך שהם מאפשרים לך לכתוב מחלקות ומתודות שעובדות עם כל טיפוס, תוך שמירה על בטיחות טיפוסים. אתה מגדיר מציין מקום (שנקרא פרמטר טיפוס) שמוחלף בטיפוס ממשי כשמשתמשים במחלקה:
// ללא טיפוסים גנריים - משתמש ב-Object, נדרשת המרת טיפוס
List list = new ArrayList();
list.add("Hello");
String s = (String) list.get(0); // נדרשת המרת טיפוס ידנית
// עם טיפוסים גנריים - בטיחות טיפוסים, ללא המרת טיפוס
List<String> list = new ArrayList<String>();
list.add("Hello");
String s = list.get(0); // אין צורך בהמרת טיפוסהסוגריים הזוויתיים <T> מציינים פרמטר טיפוס. כשכותבים List<String>, אומרים למהדר שהרשימה הזאת תכיל מחרוזות בלבד. אם מנסים להוסיף מספר שלם, המהדר מזהה את השגיאה מיד במקום שהתוכנית תיכשל בזמן הריצה.
גנריים מספקים שני יתרונות מרכזיים: הם מבטלים את הצורך בהמרות טיפוסים מפורשות, ומזהים שגיאות טיפוסים בזמן ההידור במקום בזמן הריצה. כך הקוד שלך בטוח ונקי יותר. בשיעורים הבאים נלמד איך ליצור מחלקות גנריות ומתודות גנריות משלך.
אתגר
קלבואו נבנה מערכת אחסון פשוטה שמדגימה את העוצמה של גנריות באמצעות השוואה בין הדרך הישנה (שימוש ב־Object עם המרת טיפוס) לבין הדרך החדשה (שימוש בגנריות עם בטיחות טיפוסים).
תיצרו שלושה קבצים כדי לראות את ההבדל מקרוב:
OldBox.java: צרו מחלקה ששומרת ערכים בדרך שלפני הגנריות. למחלקה OldBox צריך להיות שדה פרטיcontentמסוגObject. כללו בנאי שמקבל Object ושומר אותו, וכן מתודהgetContent()שמחזירה את ה־Object. הגישה הזאת עובדת, אך מחייבת המרת טיפוס בעת אחזור ערכים.GenericBox.java: עכשיו צרו את הגרסה המודרנית ובטוחה מבחינת טיפוסים, באמצעות גנריות. המחלקה GenericBox צריכה להשתמש בפרמטר טיפוסTבהצהרה שלה. היא צריכה לכלול שדה פרטיcontentמסוגT, בנאי שמקבל ערך מסוגT, ומתודהgetContent()שמחזירה ערך מסוגT. אין צורך בהמרת טיפוס כשמשתמשים במחלקה הזאת!Main.java: הדגימו את שתי הגישות זו לצד זו. תקבלו שתי קלטים: מילה (String) ומספר (integer).ראשית, השתמשו בגישה הישנה: צרו OldBox ששומר את המילה, אחזרו אותה עם המרת טיפוס ל־String והדפיסו:
OldBox (with cast): [value]לאחר מכן, השתמשו בגישה הגנרית: צרו
GenericBox<String>ששומר את אותה מילה, אחזרו אותה ללא המרת טיפוס והדפיסו:GenericBox (no cast): [value]לאחר מכן הדגימו שימוש בגנריות עם מספרים שלמים: צרו
GenericBox<Integer>ששומר את המספר, אחזרו אותו והדפיסו:GenericBox Integer: [value]לבסוף, הדפיסו שורה ריקה ואחריה:
Type safety: Generics catch errors at compile time!
תקבלו שתי קלטים לפי הסדר: מילה (String) ומספר (integer כמחרוזת, שאותה תצטרכו לנתח).
שימו לב להבדל המרכזי: עם OldBox, עליכם להמיר את הטיפוס באמצעות (String) box.getContent() ולקוות שקיבלתם את הטיפוס הנכון. עם GenericBox, המהדר יודע מהו הטיפוס ואין צורך בהמרת טיפוס. זהו היתרון המרכזי של גנריות: בטיחות טיפוסים בלי הטרחה הכרוכה בהמרות טיפוס!
נסו בעצמכם
import java.util.Scanner;
class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
String word = scanner.nextLine();
int number = Integer.parseInt(scanner.nextLine());
// TODO: צרו OldBox שמאחסן את המילה
// אחזרו עם המרה ל-String והדפיסו: "OldBox (with cast): [value]"
// TODO: צרו GenericBox<String> שמאחסן את המילה
// אחזרו ללא המרה והדפיסו: "GenericBox (no cast): [value]"
// TODO: צרו GenericBox<Integer> שמאחסן את המספר
// אחזרו והדפיסו: "GenericBox Integer: [value]"
// TODO: הדפיסו שורה ריקה, ואז הדפיסו:
// "Type safety: Generics catch errors at compile time!"
}
}
השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.
כל השיעורים ביחידה תכנות מונחה עצמים
1יסודות התכנות מונחה העצמים
קבצים חיצונייםמבוא לתכנות מונחה עצמיםמחלקות לעומת אובייקטיםמילת המפתח thisמתודותשדות (מאפיינים)מתודת הבנאיהעמסת בנאיםחזרה – מחשבון פשוט4ירושה
ירושה בסיסית (extends)מילת המפתח superדריסת מתודות (@Override)שרשור בנאיםהמחלקה Objectירושה יחידה ורב־רמתיתלמה אין ירושה מרובה של מחלקותסיכום – היררכיית עובדים7מתודות מיוחדות ומחלקת Object
מתודת toString()equals() ו-hashCode()מתודת clone()compareTo() ו-Comparableהממשק Comparatorסיכום – מיון מותאם אישית2מצייני גישה ואנקפסולציה
סקירה של רמות הגישהמתודות Getter ו-Setterהסתרת מידעמילת המפתח finalסיכום – מנהל חשבון בנק5פולימורפיזם
יסודות העמסת מתודותדריסת מתודות (בזמן ריצה)המרה כלפי מעלה וכלפי מטההאופרטור instanceofמחלקות ומתודות מופשטותסיכום – מחשבון צורות8מושגי OOP מתקדמים
הרכבה מול ירושהאגרגציה מול הרכבהמחלקות פנימיות, מקוננות ואנונימיותEnums ומתודות של EnumRecords (Java 16+)מחלקות חתומות (Java 17+)3מאפייני מחלקה וחברים סטטיים
משתני מופע לעומת משתנים סטטייםמתודות סטטיותבלוקים סטטייםקבועים (static final)סיכום — מונה ומחלקת עזר6ממשקים ומחלקות מופשטות
מבוא לממשקיםמימוש ממשקיםמימוש של כמה ממשקיםמתודות ברירת מחדל וסטטיות בממשקמחלקות מופשטות לעומת ממשקיםממשקים פונקציונלייםסיכום – מערכת תשלומיםתרגלו בעצמכם: קומפיילר Java אונליין