תיחום · מחלקות ותכנות מונחה־עצמים
class#
מצהיר על מרחב שמות המתנהג כמחלקת עצמים מובנית.
class מציג חבילה חדשה בדיוק כמו package, אך מסמן אותה כ־מחלקה: זמן הריצה מצרף בנאי בשם new, מאפשר את מילות המפתח field ו־method בתוך גופה, ותומך בירושה יחידה דרך התכונה :isa. זוהי התשובה ברמת שפת הליבה לתכנות מונחה־עצמים מבוסס bless שנכתב ידנית - אותה בעיה, ללא הקוד החוזר.
הערה
ניסיוני התכונה class (שם קוד Corinna) היא ניסיונית ב־Perl 5.42 ועשויה עוד להשתנות באופן בלתי תואם. יש להפעיל אותה עם use feature 'class' ולהשתיק את ההודעה עם no warnings 'experimental::class'.
תקציר#
class NAMESPACE; # rest-of-scope form
class NAMESPACE VERSION;
class NAMESPACE :ATTRIBUTES;
class NAMESPACE BLOCK # block form
class NAMESPACE VERSION BLOCK
class NAMESPACE VERSION :ATTRIBUTES BLOCK
מחלקה מינימלית עובדת:
use v5.38;
use feature 'class';
no warnings 'experimental::class';
class Point {
field $x :param;
field $y :param;
method distance_from_origin {
return sqrt($x ** 2 + $y ** 2);
}
}
my $p = Point->new(x => 3, y => 4);
say $p->distance_from_origin; # 5
מה מקבלים בחזרה#
class היא הצהרה, לא ביטוי - היא אינה מחזירה ערך הניתן להשמה. מה שמקבלים הוא:
חבילה חדשה בטבלת הסמלים, נגישה כמו כל חבילה אחרת.
בנאי
newשנוצר אוטומטית. אין לכתובnewמשלכם. הבנאי שנוצר מכבד את תכונות השדה:paramומריץ כל בלוקADJUSTבסדר ההצהרה.$selfכבול באופן מובלע בתוך כל גוףmethod.__CLASS__ניתן לשימוש בתוך מאתחלי שדה ומתודות כדי לציין את שם המחלקה הנבנית כעת (שעשויה להיות תת־מחלקה).
צורות#
class מקבלת ארבע צורות תחביריות. הפיצול בין צורות בלוק ל־משפט משקף את package בדיוק:
class NAME BLOCK- הגדרת המחלקה היא תוכן הבלוק. התיחום מסתיים בסוגר המסולסל הסוגר.class NAME;- שאר התיחום הנוכחי (או הקובץ) עד למשפטclassאוpackageהבא הוא גוף המחלקה. שימושי לקבצים בעלי מחלקה יחידה.class NAME VERSION ...- הגרסה מוצהרת כמוpackage Foo 1.234. הגרסה חייבת להקדים כל תכונה.class NAME :ATTRIBUTES ...- תכונת מחלקה אחת או יותר, מופרדות ברווחים, כל אחת מתחילה ב־:. תכונת המחלקה היחידה המוגדרת כיום היא:isa.
class My::Thing 1.02 :isa(My::Base) {
# body
}
שדות, מתודות, ADJUST#
בתוך גוף מחלקה שלוש מילות מפתח חדשות נמצאות בתיחום:
fieldמצהיר על אחסון לכל מופע. שדות מתנהגים כמו לקסיקליים בתוך מתודות אך לכל מופע יש משבצת משלו. התכונות:param,:readerו־:writerמייצרות כבילת בנאי ומאחזרים.methodמצהיר על תת־שגרה הכובלת אוטומטית את$selfויכולה לקרוא ולכתוב את השדות העוטפים לפי שם. מתודות פועלות כאילוuse feature 'signatures'בתוקף;$selfאינו מופיע בחתימה.ADJUST BLOCKרץ אחרי מאתחלי השדה, במהלך הבנייה, כאשר$selfכבר כבול. יש להשתמש בו לאימות שלאחר הבנייה ולשדות נגזרים.
class Counter {
field $value :param = 0;
field $step :param = 1;
ADJUST {
die "step must be positive" if $step <= 0;
}
method tick { $value += $step }
method peek { $value }
}
שדות הם תמיד לקסיקליים למחלקה - אינם עוברים בירושה. תת־מחלקות הזקוקות לגישה למצב של הורה עושות זאת דרך מתודות, לא דרך שדות.
ירושה: :isa#
מחלקה יורשת ממחלקה אחת אחרת לכל היותר:
class Shape { ... }
class Circle :isa(Shape) {
field $r :param;
method area { 3.14159265 * $r * $r }
}
נתמכת רק ירושה יחידה. יעד :isa נטען אוטומטית אם אינו כבר בזיכרון - שקול ל־use MODULE () לפני הצהרת המחלקה. ניתן לדרוש גרסה מינימלית באותו אופן ש־use MODULE VERSION עושה:
class Circle :isa(Shape 2.000) { ... }
:isa יורש רק מתודות. שדות הם אחסון פרטי לכל מחלקה; לתת־מחלקה אין תצוגה מובלעת אל שדותיו של הורה.
מצב גלובלי שהוא נוגע בו#
דוגמאות#
מחלקה פשוטה עם פרמטר נדרש ושדה נגזר:
class User {
field $name :param;
field $greeting;
ADJUST { $greeting = "Hello, $name" }
method greet { say $greeting }
}
User->new(name => 'Ada')->greet; # Hello, Ada
ערכי ברירת מחדל לפרמטרים - = חל כאשר הקורא השמיט את הפרמטר; //= חל גם כאשר הערך היה undef; ||= חל גם כאשר הערך היה שקר:
class Window {
field $title :param = 'untitled';
field $width :param //= 640;
field $shown :param ||= 1;
}
ירושה יחידה עם דריסת מתודה:
class Animal {
field $name :param;
method speak { "some sound" }
method introduce { say $name, " says ", $self->speak }
}
class Dog :isa(Animal) {
method speak { "woof" }
}
Dog->new(name => 'Rex')->introduce; # Rex says woof
מתודה פרטית דרך my method לקסיקלי, מופעלת דרך ->&:
class Safe {
field $secret :param;
my method check ($n) { $n == $secret }
method unlock ($n) {
return $self->&check($n) ? "open" : "denied";
}
}
שימוש ב־__CLASS__ כך שמאתחל שדה במחלקת הבסיס יקלוט את הדריסה של תת־המחלקה:
class Base {
sub DEFAULT_X { 10 }
field $x = __CLASS__->DEFAULT_X;
method x { $x }
}
class Tuned :isa(Base) {
sub DEFAULT_X { 99 }
}
say Tuned->new->x; # 99
צורת המשפט שאר־הקובץ:
use v5.38;
use feature 'class';
class Config;
field $path :param;
method path { $path }
# File ends here; whole remainder is Config's body.
מקרי קצה#
אין להגדיר
new. הבנאי שנוצר אוטומטית הוא נקודת הכניסה היחידה הנתמכת.sub new { ... }שנכתב בידי המשתמש בתוך גוףclassהוא שגיאת זמן הידור.אין ירושה מרובה.
:isa(A, B)היא שגיאת תחביר. תכונות:isaמרובות במחלקה אחת גם נדחות.אין מניפולציה של
@ISA. יש להתייחס ל־@ISAכלקריאה בלבד עבור מחלקות שהוצהרו עםclass. דחיפה אליו בזמן ריצה אינה מוגדרת.packageאינה יכולה לפתוח מחלקה מחדש. ברגע שמרחב שמות הוצהר עםclass, משפטיpackageעוקבים המכוונים לאותו שם הם שגיאת זמן הידור, ולהפך.שדות אינם לקסיקליים במובן המקובל. לא ניתן להתייחס לשדה מחוץ לגוף המחלקה, ולא ניתן להחיל עליו
ourאוlocal. רק מתודות ובלוקיADJUSTרואים שדות.$selfהוא לקריאה בלבד בתוך מתודה. השמה אליו היא שגיאת זמן ריצה.מאתחלי שדה רצים לפני שקיים
$self. בתוךfield $x = EXPR, ניתן להשתמש ב־__CLASS__ובשדות שהוצהרו קודם, אך לא ב־$self. כל מה שדורש את המופע הבנוי במלואו שייך לבלוקADJUST.אין גישה לשדה לפני הצהרה. בתוך ביטוי מאתחל של שדה, רק שדות שהוצהרו קודם לכן בגוף המחלקה נראים.
פתרון מתודה. הניתוב עדיין עובר דרך ה־MRO הרגיל (
UNIVERSAL::isa,SUPER::), כך ש־$self->SUPER::foo(@args)עובד כפי שהוא עובד עבור מחלקות מבוססותbless.תיחום צורת המשפט.
class Foo;צורך את שאר הבלוק העוטף - בדרך כלל שאר הקובץ.classשני אוpackageמסיים אותו; אין דרך ״לסגור״ מחלקה בצורת משפט מוקדם.אזהרת ניסיוני. כל הצהרת
classמנפיקה אזהרתclass is experimentalתחת האזהרות בברירת מחדל. יש להשתמש ב־no warnings 'experimental::class'ברגע שהתכונה בשימוש אמיתי.
הבדלים מהמקור#
תואם במלואו ל־Perl 5.42 במקור. התכונה מסומנת כניסיונית במקור; pperl משקף את הסטטוס הזה ומנפיק את אותה קטגוריית אזהרה (experimental::class).
ראו גם#
field- מצהיר על אחסון לכל מופע בתוך גוףclass; תומך ב־:param,:reader,:writermethod- מצהיר על תת־שגרה עם$selfמובלע וגישה ישירה לשדות המחלקה העוטפתpackage- הצהרת מרחב השמות שאינה מחלקתית;classהוא האח המודע לתכנות מונחה־עצמים בעל אותן צורות תחביריותbless- הדרך עד גרסה 5.38 לצרף מחלקה להפניה; עדיין נתמך, ומשתלב עם עצמים בנוייclassלבדיקותisa/canisa- אופרטור לבדיקת חברות במחלקה; עובד גם עבור עצמים שהוצהרו עםclassוגם עבור עצמים מבוססיblessuse- כיצד:isa(Parent)טוען את מחלקת ההורה מאחורי הקלעים