java - איך להגדיר חתימת בונה בממשק?
להלן השאלה לגבי הגדרת חתימת בונה בממשק TypeScript (Javascript), אך בסופו של דבר התברר שזה לא זמין. עם זאת, זה אפשרי ב-Objective-C וזה יהיה נחמד שיהיה זמין גם במקומות אחרים.
יש לי סט של מחלקות אובייקטי נתונים וכולן ישמשו דרך בונה משותף עם פרמטר של מחרוזת JSON. מכיוון שבחרתי ב-TypeScript ככלי שיש לו קומפיילר ויעזור לי למצוא טעויות ובעיות מוקדם יותר, איך אני יכול להגדיר בונה בממשק ואז לממש אותו במחלקה?
interface IParsable {
//constructor(json : string);
new (json : string);
}
class UserObject implements IParsable {
constructor(json : string) {
//...
}
הקוד עובד אבל המהדר מציג לי את השגיאה "שגיאה TS2420: המחלקה 'UserObject' מממשת בצורה שגויה את הממשק 'IParsable'." אם אני משתמש ב"constructor" במקום new בממשק אז מקבל את ההודעה "שיטת constructor מהממשק IParsable לא ממומשת".
עדכון: ב-Objective-C, ניתן ליצור אובייקט בשתי דרכים:
הגישה הראשונה:
UserObject *userObject = [[UserObject alloc] init];
יצירת אובייקט מורכבת משני שיטות. Alloc - מקצה את הזיכרון הנחוץ ו-init הוא שם הקונסטרקטור הבררת מחדל. קונסטרקטורים הם כמו שיטות רגילות ב-objective-c ויש להם שמותיהם. לכן, אם אתה צריך קונסטרקטור מותאם אישית עם פרמטרים, אתה פשוט מגדיר שיטה חדשה, לדוגמה initWithJson:(NSString *)json. לכן אתה יכול להגדיר כל מה שאתה רוצה בממשק ולאכוף את החוזה.
הגישה השנייה:
UserObject *userObject = [UserObject new];
"new" היא מילה שמורה, אך למעשה היא מבצעת alloc + init ולא ניתן להשתמש בקונסטרקטור מותאם אישית שלך. לכן הדרך המומלצת היא להשתמש ב-alloc + init, כך שתמיד תוכל להחליף אותו בשיטת הקונסטרקטור המותאמת אישית שלך כמו alloc + initWithJson אם צריך.
כפי שמתברר, אין דרך לעשות את זה אפילו בג'אווה או ב-C#. תמיד חשבתי שהשפות הללו הרבה יותר סטנדרטיות ומחושבות היטב במונחי OOP, למרות שהדבר הקטן הזה עדיף לעשות ב-Objective-C.
תשובה #1
סעיף extends מגדיר את ההתנהגות של מופע של מחלקה, לא של פונקציית הבנאי שלה. הבנאים עצמם אינם חלק מ-המופע של המחלקה. במובן מהותי, אתה אומר שמופע של UserObject (התוצאה של קריאה ל-new עליו) יכול בעצמו להיות מופעל עם new, וזה אינו המקרה:
var x = new UserObject();
var y = new x(); // Nonsense, of course
נכון לעכשיו אין תחביר ישיר ב-TypeScript שמציין שפונקציית הבנאי UserObject מתאימה לממשק IParsable. ניתן להשתמש בכך כפתרון עקיף:
class UserObject { ... }
UserObject === <IParsable>undefined;
תשובה #2
TypeScript היא שפה עם טיפוס מבני, כך שתוכל לקבל את כל הבדיקות שאתה רוצה מבלי שצריך להשתמש בפועל במילת המפתח implements על מחלקה.
בדוגמה למטה, הפרמטרים המועברים לבונה של IParsable נבדקים (בתוך הפונקציה example). בנוסף, אינך יכול למסור ארגומנט לפונקציה example שאינו עומד בממשק IParsable, כלומר הוא חייב לקבל ארגומנט מסוג מחרוזת (או שלא לקבל ארגומנט כלל).
סימנתי שלוש שגיאות שהקומפיילר יתפוס.
interface IParsable {
new (json: string);
}
class ExampleOne {
constructor(json: string) {
}
}
class ExampleTwo {
constructor(json: string) {
}
}
class ExampleThree {
constructor(arg: number) {
}
}
function example(obj: IParsable) {
// Error - doesn't pass string arg
//return new obj();
// Error - wrong argument type
//return new obj(1);
return new obj('{ a: "b"}');
}
example(ExampleOne);
example(ExampleTwo);
// Error - Doesn't satisfy IParsable
//example(ExampleThree);
