Menu
Coddy logo textTech

מבוא לגנריות

חלק מהיחידה תכנות מונחה עצמים במסלול ה-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>, אומרים למהדר שהרשימה הזאת תכיל מחרוזות בלבד. אם מנסים להוסיף מספר שלם, המהדר מזהה את השגיאה מיד במקום שהתוכנית תיכשל בזמן הריצה.

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

challenge icon

אתגר

קל

בואו נבנה מערכת אחסון פשוטה שמדגימה את העוצמה של גנריות באמצעות השוואה בין הדרך הישנה (שימוש ב־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!"
    }
}
quiz iconבחנו את עצמכם

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

כל השיעורים ביחידה תכנות מונחה עצמים

תרגלו בעצמכם: קומפיילר Java אונליין