יום שישי, 15 בנובמבר 2019

הסבר מתומצת על Casting בשפות מונחות עצמים

נסביר על התכונות הבסיסיות של Casting. כל הדוגמאות יהיו בג'אווה, אבל העקרונות נכונים בעוד הרבה שפות טיפוסיות (Statically Typed Languages) מונחות עצמים.


 Casting

נניח שיש לנו מחלקה משם Animal ויורשת ממנה מחלקה בשם Bird.

מצביע מסוג Animal יכול להצביע על Animal כמובן, אבל הוא יכול להצביע גם על היורשים, ולכן גם על Bird.

    Animal myAnimal1 = new Animal();
    Animal myAnimal2 = new Bird();

פעולה זו נקראת Upcasting, והיא הבסיס לפולימרפיזם.



לאחר ההמרה myAnimal2 לא יכול לקרוא לפונקציות של Bird, מאחר והחיה הזו שכחה שהיא הייתה פעם ציפור. אמנם היא עודנה ציפור וניתן להזכיר לה זאת על ידי Downcasting.


מהו Downcasting?
ההפך של Upcasting. הצבעה על אובייקט עם מצביע ממחלקה שיורשת ממחלקת האובייקט.
Downcasting לעולם יבוא רק לאחר שנעשה Upcasting.

    Bird myBird = (Bird) myAnimal2;

אחרי Downcasting ניתן לקרוא לפונקציות של Bird.

    myBird.fly();





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

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


נדגים:

public class Engineer {
    public void doEngineering() {
        System.out.println("I do some engineering");
    }
}

public class SmartEngineer extends Engineer {
    @Override
    public void doEngineering() {
        System.out.println("I do some engineering (but I am smart!)");
    }
    public void doSmartEngineering() {
        System.out.println("I do some smart engineering");
    }
}

public static void main(String[] args) {
    String userInput = "normal";
        
    Engineer engineer = null;
        
    if (userInput.equals("normal")) {
        engineer = new Engineer();
    }
    else {
        engineer = new SmartEngineer();
    }

    // We can do:
    engineer.doEngineering();

    // We can't do (compilation error):
    // engineer.doSmartEngineering();


    // We can do it but it's risky (If userInput is "normal" we get ClassCastException):

    ((SmartEngineer) engineer).doSmartEngineering();
}


מקווה שהכל ברור.