7

ג'אווה - תכנות מונחה עצמים - מחלקות פנימיות - רגילות, אנונימיות וסטטיות

  • Upload
    -

  • View
    1.019

  • Download
    10

Embed Size (px)

DESCRIPTION

חלק מסדרת הרצאות בנושא "תכנות מונחה עצמים בסביבת ג'אווה", והפעם: מחלקות פנימיות.

Citation preview

Page 2: ג'אווה - תכנות מונחה עצמים - מחלקות פנימיות - רגילות, אנונימיות וסטטיות

Inner Classes – מחלקות פנימיות

מחלקה פנימית היא מחלקה שמוגדרת בתוך מחלקה אחרת ועל כן יש למופע שלה גישה למשתנים ולמתודות של האובייקט

כמחלקת עזר למחלקה החיצונית בה . לרוב תשמש מחלקה זו privateשבנוכחותו הוא נוצר, גם לאלו בעלי מציין הגישה

היא מוגדרת. מחלקה פנימית תוגדר באופן הבא:

public class OuterClass { class InnerClass { } }

לא ניתן ליצור מופע של מחלקה פנימית מבלי אותה נוכחות של אובייקט מהמחלקה המכילה, אלא אם כן מגדירים אותה

כסטטית ואז הדבר מתאפשר.

תן להשיג נוכחות זו בשלוש דרכים:ני

, ע"י יצירת אובייקט מהמחלקה הפנימית בקונסטרקטור של המחלקה החיצונית )ואז ברור מי הוא האובייקט הראשונה

החיצוני(, לאובייקט זה תהיה לנו גישה בטווח ההכרה של הקונסטרקטור:

public class OuterClass { public OuterClass() { InnerClass innerObject1=new InnerClass(); } class InnerClass { } }

, ע"י יצירת אובייקט מהמחלקה הפנימית בתוך מתודת מופע של האובייקט החיצוני )וגם אז ברור מיהו האובייקט השניה

החיצוני(, אז גם ניתן להמשיך ולהחזיק ברפרנס לאותו אובייקט מחלקה פנימית:

public class OuterClass { public InnerClass getInnerObj() { return new InnerClass(); } class InnerClass { } }

OuterClass outerObject=new OuterClass(); //In OuterClass.InnerClass innerObject2=outerObject.getInnerObj(); //Main...

ברפרנס לאובייקט מהמחלקה החיצונית )גם כאן כמובן ברור מיהו אותו אובייקט מהמחלקה החיצונית , ע"י שימוש השלישית

בנוכחותו הוא נוצר(:

OuterClass.InnerClass innerObject3=outerObject.new InnerClass(); //In Main... OR

OuterClass.InnerClass innerObject4=new OuterClass().new InnerClass(); //In Main...

Page 3: ג'אווה - תכנות מונחה עצמים - מחלקות פנימיות - רגילות, אנונימיות וסטטיות

הקשר בין האובייקט של המחלקה הפנימית לבין זה של המחלקה החיצונית טמון ברפרנס לאובייקט מהמחלקה החיצונית

שהאובייקט של המחלקה הפנימית מחזיק בצורה נסתרת מאחורי הקלעים.

ר הסתרה של המחלקה ע"י מציין עבור מחלקות פנימיות, ניתן להשתמש בכל מצייני הגישה הקיימים ובכך לדוגמה, לאפש

, כך שהמשתמש במחלקה החיצונית לא ידע כלל על המתחולל בטווח ההגדרה של המחלקה שהוא מכיר, privateהגישה

פעולת המחלקה הפנימית תהיה מוסתרת ולרוב תבוא להקל על פעולתה של החיצונית מבלי שהמשתמש בה ידע עליה כלל.

דה, כזו שתעזור לפעולתה של המתודה בלבד וטווח ההכרה שלה יהיה לאותה המתודה ניתן להגדיר מחלקה פנימית במתו

בה היא הוגדרה בלבד, עם זאת, למחלקה פנימית במתודה תתאפשר גישה למשתנה של המתודה רק אם הוא הוגדר כקבוע

finalתנים המקומיים עבור . דרישה זו חלה, היות ובעת היצירה של המחלקה הפנימית בתוך המתודה נוצר עותק של המש

המחלקה הפנימית. אם אלו לא יוגדרו כסופיים, אין מניעה מהם להשתנות במהלך פעולת המתודה ואז לא יהיה קשר בין

המשתנה של המתודה למשתנה של המחלקה הפנימית למתודה, והרי שהם אמורים להיות זהים! הודות לאופיים של

ההכרה של מית זו יכיר את המשתנה המקומי הסופי גם ביציאה מטווח משתנים סופיים, אובייקט שנוצר ממחלקה פני

המתודה!

public class OuterClass { public void defineMethodInnerClass() { int var=0; final int finalVar=0; class methodInnerClass { public methodInnerClass() { //System.out.println(var); *ERROR* System.out.println(finalVar); //*OK* } } } }

ע"מ לקבל רפרנס לאובייקט הפנימי שנוצר, או thisבתוך מתודות של המחלקה הפנימית ניתן להשתמש במילה השמורה

לאובייקט החיצוני בנוכחותו האובייקט של הפנימית נוצר: ע"מ לקבל רפרנס thisבשם המחלקה החיצונית, נקודה ואז

public class OuterClass { private int sum=0; public void setSum(int sum) { this.sum=sum; } class InnerClass { private int sum=0; public void incInnerSum() { this.sum++; } public void incOuterSum() { OuterClass.this.sum++; } } }

Page 4: ג'אווה - תכנות מונחה עצמים - מחלקות פנימיות - רגילות, אנונימיות וסטטיות

מבחינת שם המחלקה, כאשר אנו נמצאים בטווח ההכרה של המחלקה העוטפת, מספיק לקרוא למחלקה הפנימית בשמה,

מפרידה אך כאשר מחוץ לטווח הכרה זה, יש לקרוא למחלקה הפנימית בצירוף שם המחלקה החיצונית לפני שמה ונקודה

(Full Qualified Name.)

כאשר מדובר במחלקות פנימיות סטטיות )נסביר בהמשך(, יש דרך לקצר את אופן הפניה.

מבחינת קובץ המחלקה, אין הבדל בין קבצי המחלקות הפנימיות לבין קבצי המחלקות הרגילות מלבד שינוי קטן במבנה שם

חלקה פנימית לבין מחלקה חיצונית:, אין הבדל בין מJVM-הקובץ, זאת היות ומבחינת ה

OuterClass.classייראה כך: OuterClassקובץ המחלקה החיצונית

.OuterClass$InnerClass.classשמוגדרת בתוכה ייראה כך: InnerClassקובץ המחלקה הפנימית

נושא שיוסבר לפרטים בהמשך. - Reflection -נעמיק בעניין זה כאשר נבצע שיקוף

Anonymous Inner Classes – פנימיות אנונימיותמחלקות

מחלקה פנימית יכולה להיות חסרת שם וחסרת פונקציה בונה )קונסטרקטור(, שלרוב נוצר ממנה אובייקט אחד ויחיד, ולא

ניתן לייצר באופן ידני מופעים נוספים שלה. זוהי מחלקה "מנוונת" במובן מסוים שלרוב מבצעת "עבודה מזדמנת" מוגדרת

מראש.

נציין ראשית, שאין מבחינה טכנית בעיה להשתמש במחלקה פנימית רגילה בכל אותם המקרים בהם נבחר להשתמש

במחלקה פנימית אנונימית, זהו עניין אסטטי יותר מכל דבר אחר. לרוב, עם זאת, יהיה זה מיותר להגדיר מחלקה "כדת

שכזו. וכדין" לצורך ביצוע משימה חד פעמית שאינה דורשת מחלקה

ע"מ לייצר את אותו אובייקט מחלקה פנימית אנונימית נרשום:

new Type(/*ARGUMENTS*/) { /*Methods and Variables that extend/implement class/interface Type*/

};

.Typeונקבל חזרה רפרנס לאובייקט חדש שנוצר מאותה מחלקה אנונימית אותו נציב במשתנה מסוג

פעמי כמובן שהרי הוא לא מכיר -הזה, אנחנו לא נוכל לפנות למשתנים/מתודות שהגדרנו בטיפוס החדש החד דרך המשתנה

אותם ולא יכול לראות אותם, ולכן, ע"מ שנוכל להשתמש באותו טיפוס חדש, לפחות מתודה אחת מהטיפוס ששימש אותנו

החדשות והמשתנים שהגדרנו.כבסיס צריכה להידרס. דרך מתודה זו ניתן כבר להשתמש בכל המתודות

Type יכול להיות מטיפוס אינטרפייס שהמחלקה הפנימית האנונימית מיישמת )אבל אז לא שולחים ארגומנטים(, ו-Type גם

יכול להיות מחלקה שהמחלקה הפנימית האנונימית מרחיבה.

Page 5: ג'אווה - תכנות מונחה עצמים - מחלקות פנימיות - רגילות, אנונימיות וסטטיות

נראה דוגמה למימוש של מחלקה פנימית אנונימית.

והגדרת המחלקות:נבחן את היררכיית

public class Human { public void cout() { System.out.println("I'm a Human!"); } }

public class Person extends Human { public void use() { Human human1=new Human(); Human human2=new Human(/*No Arguments - Default Constructor @ class Human*/) { /*Variables that extend class Human*/ int unique=10; /*Methods that extend class Human*/ public void cout() { super.cout(); System.out.println("But not just a Human..."); cantAccessDirectly(unique); } public void cantAccessDirectly(int u) { System.out.println("I'm a special kind of Human!"); System.out.println("My Unique ID is: "+u); } }; System.out.println("******************************"); System.out.println("cout of human1:"); human1.cout(); System.out.println("******************************"); System.out.println("cout of human2:"); human2.cout(); System.out.println("******************************"); } }

Page 6: ג'אווה - תכנות מונחה עצמים - מחלקות פנימיות - רגילות, אנונימיות וסטטיות

ונקבל את הפלט הבא: ()use, נפעיל עליו את Personאובייקט מטיפוס main-ניצור ב

****************************** cout of human1: I'm a Human! ****************************** cout of human2: I'm a Human! But not just a Human... I'm a special kind of Human! My Unique ID is: 10 ******************************

והשני ממחלקה פנימית Human, הראשון נוצר מהמחלקה human2-ו human1נשים לב להבדלים בין יצירת האובייקט

.Humanאנונימית, אשר מרחיבה באופן רגעי את המחלקה

מתודה מוכרת מהמחלקה , זאת ע"מ לאפשר הפעלה של ()coutבמחלקה הפנימית האנונימית דאגנו לדרוס את המתודה

Human כך שכאשר נרצה להפעיל את ,cout() דרךhuman1 נפעיל את ,cout() שלHuman הסטנדרטית, אך כאשר נפעיל

, נפעיל את זו שנדרסה במחלקה הפנימית האנונימית.human2של ()coutאת

במחלקה הפנימית האנונימית, לדוגמה הדרוסה, נוכל להפעיל מתודות נוספות שהוגדרו ()coutבאופן זה, דרך המתודה

cantAccessDirectly() שכשמה כן היא, מתודה שלא נוכל לגשת אליה באופן ישיר דרך מופע המחלקה ,human2 אלא רק ,

.()coutדרך המתודות האחרות במחלקה ובראשן

ושים בו שימוש הוא כאמור, ההעדפה לבצע שימוש במחלקה פנימית אנונימית תהיה במצבים בהם האובייקט שאנו ע

פעמי", אובייקט כזה שעושה עבודה מסוימת ולא צריך בו יותר, או אפילו כזה שרץ משך כל התכנית, אך אין -אובייקט "חד

צורך ממשי או לוגי להחזיק ביותר מאובייקט אחד כזה.

( הרצים threadsהליכונים )ת-ניקח לדוגמה מקרה בו אנו כותבים תכנית המהווה משחק מחשב וברצוננו להחזיק בשני חוטים

במקביל: האחד, מריץ את ממשק המשתמש הגרפי והשני אחראי להרצת הלוגיקה של חישובי המהלכים של המחשב

)המחשב יחשב מהלכים משלו בזמן התור של המשתמש(.

ם אין צורך אמתי בזמן שהמחשב "חושב", זהו תהליכון ספציפי, ואין צורך בעוד תהליכון שיעשה את אותה העבודה ולכן, ג

כדי שנשתמש בה רק thread-במחלקה שתייצג את התהליכון. במקרים כאלו, ניתן לוותר על יצירה של מחלקה שתירש מ

פעם אחת בתחילת ההרצה, במקום זה, מספיק לנו ליצור אובייקט חד פעמי שאותו נשלח לתהליכון ובזאת סיימנו, אין עוד

ת...צורך באובייקטים כאלו עד סוף התכני

אובייקט ממחלקה, יותר ייצרלסיכום, ניתן לשייך את הצורך במחלקה פנימית אנונימית למקרים בהם אנו רוצים ל

מאשר מחלקה ממנה ניתן לייצר אובייקטים.

Page 7: ג'אווה - תכנות מונחה עצמים - מחלקות פנימיות - רגילות, אנונימיות וסטטיות

Static Nested Classes –מחלקות פנימיות סטטיות

מחלקה פנימית ניתנת להגדרה כסטטית )בניגוד למחלקה רגילה(.

ממחלקה פנימית סטטית יווצר אובייקט גם ללא נוכחות של אובייקט מהמחלקה המכילה, כלומר אובייקט מסוג מחלקה זו

אינו קשור לאובייקט מהמחלקה החיצונית ולא יכיל רפרנס לאובייקט כזה.

( דרך מתודה בהמשך StaticNestedClassבאם נרצה לייצר אובייקט של מחלקה פנימית )לדוגמה אובייקט של המחלקה

, על המחלקה הפנימית להיות מוגדרת כסטטית, באופן הבא:mainסטטית של מחלקה אחרת, לדוגמת במתודת

public class OuterClass { static class StaticNestedClass { public void staticNestedMethod() { } } }

תתבצע כך: main-יצירת האובייקט ב

OuterClass.StaticNestedClass staticNestedObject1=new OuterClass.StaticNestedClass(); staticNestedObject1.staticNestedMethod();

של המחלקה הסטטית לקובץ המחלקה import, ע"י ביצוע של Full Qualified Name-כאמור, ניתן להימנע משימוש ב

(.main-המריצה )בה נמצאת מתודת ה

יש לציין שלשם ביצוע ייבוא של מחלקה סטטית, המחלקות צריכות להשתייך לחבילה שונה מהחבילה הדפולטיבית.

על כן, נשייך את הקבצים לחבילות ונבצע ייבוא כך:

package somepackage; import staticimport.OuterClass.StaticNestedClass;

נימית הסטטית כאילו הייתה במעלה ההיררכיה:ניתן יהיה לפנות למחלקה הפומאותו הרגע,

StaticNestedClass staticNestedObject2=new StaticNestedClass(); staticNestedObject2.staticNestedMethod();

המקרים בהם נשתמש במחלקה פנימית סטטית, הם מקרים די ייחודיים. יש יתרון לשימוש בה במקרים בהם אובייקט שנייצר

במחלקה העוטפת. private-צריך לגשת לשדה סטטי שהוגדר כ ממנה

כשיקול מקדים בהחלטה באם תהיה המחלקה הפנימית שלנו סטטית או לא, נשאל את עצמנו האם יש לנו צורך לכלול בטווח

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

שהרי נקבל שגיאת קומפילציה. סטטיתשלא לנסות ולהגדירה כ

מבחינה היררכית, מחלקה סטטית נחשבת למחלקה עצמאית שיושבת בראש היררכיה מבחינה לוגית, היא –יש לזכור

ממוקמת בתחום הכרה של מחלקה אחרת רק לשם סדר ונוחיות.