מחלקות ומתודות מופשטות
חלק מהיחידה תכנות מונחה עצמים במסלול ה-Java של Coddy. שיעור 32 מתוך 87.
לפעמים רוצים להגדיר מחלקה שמשמשת כתבנית, אך לעולם לא אמורים ליצור מופע שלה ישירות. לדוגמה, מחלקת Shape כללית הגיונית כמחלקת־אב, אבל איך תיראה "צורה" שאין לה צורה מסוימת? כאן נכנסות לתמונה מחלקות מופשטות.
מחלקה מופשטת מוכרזת באמצעות מילת המפתח abstract ואי אפשר ליצור ממנה מופע. היא יכולה להכיל גם מתודות רגילות עם מימוש וגם מתודות מופשטות שאין להן גוף:
public abstract class Shape {
protected String color;
public Shape(String color) {
this.color = color;
}
// מתודה מופשטת - ללא מימוש
public abstract double getArea();
// מתודה רגילה - יש לה מימוש
public String getColor() {
return color;
}
}מתודות מופשטות מצהירות מה תת־מחלקה חייבת לעשות, אך לא איך. כל מחלקה שאינה מופשטת ומרחיבה מחלקה מופשטת חייבת לספק מימושים לכל המתודות המופשטות:
public class Circle extends Shape {
private double radius;
public Circle(String color, double radius) {
super(color);
this.radius = radius;
}
@Override
public double getArea() {
return Math.PI * radius * radius;
}
}עדיין אפשר להשתמש במחלקות מופשטות כסוגי ייחוס, וכך לאפשר פולימורפיזם:
Shape shape = new Circle("red", 5.0);
System.out.println(shape.getArea()); // קורא למימוש של Circleמחלקות מופשטות אידיאליות כאשר מחלקות יורשות חולקות קוד משותף, אך כל אחת מהן חייבת לממש התנהגויות מסוימות באופן שונה. הן אוכפות חוזה תוך שהן מאפשרות שימוש חוזר בקוד.
אתגר
קלבואו נבנה מערכת למכשירי חשמל שמדגימה את העוצמה של מחלקות מופשטות. תיצרו מחלקת בסיס Appliance שמגדירה מה כל מכשיר חשמלי חייב לעשות, ותאפשרו לסוגים מסוימים של מכשירים להחליט בדיוק איך הם עושים זאת.
תארגנו את הקוד שלכם בארבעה קבצים:
Appliance.java: צרו מחלקה מופשטת שמשמשת כתבנית לכל מכשירי החשמל. לכל מכשיר חשמלי יש שדהbrand(String) ושדהwattage(int). כללו בנאי שמאתחל את שני השדות, שיטות getter לכל אחד מהם, ושיטה רגילהgetInfo()שמחזירה:[brand] - [wattage]W. הגדירו שיטה מופשטתoperate()שמחלקות יורשות חייבות לממש — היא מייצגת את האופן שבו כל מכשיר חשמלי מבצע את הפעולה העיקרית שלו.WashingMachine.java: צרו מחלקה קונקרטית שיורשת מ-Appliance. למכונות כביסה יש שדה נוסףcapacity(int) שמייצג את גודל המטען בק״ג. השתמשו ב-superכדי לטפל באתחול של מחלקת האב. ממשו את השיטהoperate()כך שתדפיס:[brand] washing machine is washing [capacity]kg of clothesMicrowave.java: צרו מחלקה קונקרטית נוספת שיורשת מ-Appliance. למיקרוגלים יש שדהpower(int) שמייצג את אחוז עוצמת ההספק. ממשו אתoperate()כך שתדפיס:[brand] microwave is heating at [power]% powerMain.java: חברו את הכול יחד באמצעות פולימורפיזם. תקבלו חמש קלטים: שם מותג, הספק בוואטים, קיבולת מכונת הכביסה, שם מותג נוסף ורמת ההספק של המיקרוגל. צרו WashingMachine ו-Microwave, ואז אחסנו אותם במערך של הפניות מסוגAppliance. עברו בלולאה על המערך, ולכל מכשיר חשמלי הדפיסו את המידע שלו באמצעותgetInfo()ולאחר מכן קראו ל-operate().
תקבלו חמישה קלטים בסדר הזה: מותג ראשון (String), הספק ראשון בוואטים (int), קיבולת בק״ג (int), מותג שני (String) ורמת הספק (int).
הפלט שלכם צריך לכלול ארבע שורות — עבור כל מכשיר חשמלי, תראו שורת מידע ואחריה את אופן הפעולה שלו. שימו לב איך המחלקה המופשטת מאפשרת להתייחס באופן אחיד למכשירי חשמל שונים, בעוד שכל אחד מהם פועל בדרך הייחודית לו!
נסו בעצמכם
import java.util.Scanner;
class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// קראו את נתוני הקלט
String brand1 = scanner.nextLine();
int wattage1 = scanner.nextInt();
int capacity = scanner.nextInt();
scanner.nextLine(); // צרכו את תו ירידת השורה
String brand2 = scanner.nextLine();
int power = scanner.nextInt();
// TODO: צרו אובייקט מסוג WashingMachine עם brand1, wattage1 ו-capacity
// TODO: צרו אובייקט מסוג Microwave עם brand2, wattage1 (או הספק מתאים) ו-power
// TODO: צרו מערך של הפניות מסוג Appliance ואחסנו בו את שני האובייקטים
// TODO: עברו על המערך, ועבור כל מכשיר:
// - הדפיסו את המידע עליו באמצעות getInfo()
// - קראו ל-operate()
}
}
השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.
כל השיעורים ביחידה תכנות מונחה עצמים
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 אונליין