מחלקות חתומות (Java 17+)
חלק מהיחידה תכנות מונחה עצמים במסלול ה-Java של Coddy. שיעור 52 מתוך 87.
ב-Java המסורתית, כשאתה יוצר מחלקה, כל מחלקה אחרת יכולה לרשת ממנה (אלא אם כן מסמנים אותה בתור final). אבל מה אם רוצים לאפשר ירושה, רק לא מכל אחד? Java 17 הציגה מחלקות אטומות כדי לתת לך שליטה מדויקת על אילו מחלקות יכולות לרשת מהמחלקה שלך.
מחלקה חתומה מצהירה במפורש על תת-המחלקות המורשות שלה באמצעות מילות המפתח sealed ו-permits:
sealed abstract class Shape permits Circle, Rectangle, Triangle {
abstract double area();
}
final class Circle extends Shape {
double radius;
double area() { return Math.PI * radius * radius; }
}
final class Rectangle extends Shape {
double width, height;
double area() { return width * height; }
}
final class Triangle extends Shape {
double base, height;
double area() { return 0.5 * base * height; }
}רק Circle, Rectangle ו-Triangle יכולים לרשת את Shape. כל מחלקה אחרת שתנסה לרשת אותה תגרום לשגיאת הידור.
כל מחלקת משנה מותרת חייבת להשתמש באחד משלושה משני תכונות: final (לא ניתן להרחיב עוד), sealed (ממשיכה את שרשרת ההגבלות), או non-sealed (מאפשרת הרחבה ללא הגבלות):
sealed class Vehicle permits Car, Truck { }
final class Car extends Vehicle { } // לא ניתן לרשת ממנה
non-sealed class Truck extends Vehicle { } // כל אחד יכול לרשת מ־Truckמחלקות אטומות עוצמתיות במיוחד בשילוב עם התאמת תבניות בביטויי switch, מכיוון שהמהדר מכיר את כל תתי־הסוגים האפשריים ויכול לוודא שהכיסוי מלא. הן אידיאליות למידול של קבוצה קבועה של סוגים בתחום מסוים, כמו אמצעי תשלום, סוגי תגובות או צורות גאומטריות.
אתגר
קלבואו נבנה מערכת התראות שמדגימה מחלקות sealed באמצעות הגבלת סוגי ההתראות שיכולים להתקיים באפליקציה שלך. תיצור היררכיה שבה מותרים רק סוגי התראות מסוימים, וכל סוג מטפל בשליחה באופן שונה.
תארגן את הקוד שלך בחמישה קבצים:
Notification.java: צור מחלקה אבסטרקטית sealed שמשמשת כמחלקת הבסיס לכל ההתראות. היא צריכה להתיר בדיוק שלוש מחלקות משנה:EmailNotification,SMSNotificationו-PushNotification. המחלקה ה-sealed שלך צריכה לכלול שדה מוגןmessage(String), בנאי לאתחול שלו, getter להודעה ומתודה אבסטרקטיתdeliver()שמחזירה String המתאר כיצד ההתראה נשלחת.EmailNotification.java: צור מחלקהfinalשיורשת מ-Notification. ל-EmailNotification יש שדה פרטי נוסףrecipient(String) עבור כתובת האימייל. הבנאי שלה צריך לקבל גם את ההודעה וגם את הנמען, ולקרוא לבנאי של מחלקת האב באמצעותsuper(). המתודהdeliver()צריכה להחזיר:Sending email to [recipient]: [message]SMSNotification.java: צור מחלקהnon-sealedשיורשת מ-Notification. כך מחלקות אחרות יוכלו לרשת ממנה אם יהיה בכך צורך בעתיד. ל-SMSNotification יש שדה פרטי נוסףphoneNumber(String). הבנאי שלה מקבל את ההודעה ואת מספר הטלפון. המתודהdeliver()צריכה להחזיר:Sending SMS to [phoneNumber]: [message]PushNotification.java: צור מחלקהfinalשיורשת מ-Notification. ל-PushNotification יש שדה פרטי נוסףdeviceId(String). הבנאי שלה מקבל את ההודעה ואת מזהה המכשיר. המתודהdeliver()צריכה להחזיר:Sending push to device [deviceId]: [message]Main.java: חבר את מערכת ההתראות שלך! תקבל ארבעה קלטים: הודעה (String), כתובת אימייל (String), מספר טלפון (String) ומזהה מכשיר (String).צור התראה אחת מכל סוג, באמצעות אותה הודעה אך עם פרטי הקשר המתאימים לכל סוג. אחסן את שלושתן במערך מסוג
Notification[]כדי להדגים פולימורפיזם עם מחלקות sealed. לאחר מכן עבור על המערך והדפס את התוצאה של קריאה ל-deliver()בכל התראה.
תקבל ארבעה קלטים לפי הסדר: הודעה, כתובת אימייל, מספר טלפון ומזהה מכשיר.
שים לב כיצד המחלקה sealed מגבילה את ההיררכיה: רק שלוש המחלקות המורשות יכולות לרשת מ-Notification. כל מחלקת משנה חייבת להצהיר שהיא final, sealed או non-sealed. כך תהיה לך שליטה מלאה בהיררכיית הטיפוסים שלך, תוך שמירה על האפשרות להשתמש בפולימורפיזם!
נסו בעצמכם
import java.util.Scanner;
class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// קראו את ארבעת ערכי הקלט
String message = scanner.nextLine();
String email = scanner.nextLine();
String phoneNumber = scanner.nextLine();
String deviceId = scanner.nextLine();
// TODO: צרו מופע אחד מכל סוג התראה באמצעות אותה הודעה
// - EmailNotification עם message ו-email
// - SMSNotification עם message ו-phoneNumber
// - PushNotification עם message ו-deviceId
// TODO: אחסנו את כל שלוש ההתראות במערך Notification[]
// כך מודגמת פולימורפיות באמצעות מחלקות חתומות
// TODO: עברו על המערך והדפיסו את התוצאה של deliver()
// עבור כל התראה
}
}
השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.
כל השיעורים ביחידה תכנות מונחה עצמים
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 אונליין