CodeGym /בלוג Java /Random-HE /כיתות פנימיות מקוננות
John Squirrels
רָמָה
San Francisco

כיתות פנימיות מקוננות

פורסם בקבוצה
היי! היום נתייחס לנושא חשוב - איך מחלקות מקוננות עובדות ב-Java. Java מאפשרת לך ליצור מחלקות בתוך מחלקה אחרת:
class OuterClass {
    ...
    static class StaticNestedClass {
        ...
    }
    class InnerClass {
        ...
    }
}
מחלקות פנימיות אלו נקראות מקוננות. הם מחולקים ל-2 סוגים:
  1. מחלקות מקוננות לא סטטיות. אלה נקראים גם כיתות פנימיות.
  2. מחלקות מקוננות סטטיות.
בתורו, למחלקות פנימיות יש שתי קטגוריות משנה נפרדות. בנוסף לכך שכיתה פנימית היא פשוט כיתה פנימית, היא יכולה להיות גם:
  • כיתה מקומית
  • כיתה אנונימית
מְבוּלבָּל? :) זה בסדר. להלן תרשים לבהירות. חזור אליו במהלך השיעור אם אתה פתאום מוצא את עצמך מבולבל! כיתות פנימיות מקוננות - 2בשיעור של היום, נדון בשיעורים פנימיים (הידועים גם בתור מחלקות מקוננות לא סטטיות). הם מודגשים במיוחד בתרשים הכללי כדי שלא תלכו לאיבוד :) נתחיל בשאלה המתבקשת: למה הם נקראים כיתות "פנימיות"? התשובה די פשוטה: מכיוון שהם נוצרים בתוך מחלקות אחרות. הנה דוגמא:
public class Bicycle {

   private String model;
   private int weight;

   public Bicycle(String model, int weight) {
       this.model = model;
       this.weight = weight;
   }

   public void start() {
       System.out.println("Let's go!");
   }

   public class Handlebar {

       public void right() {
           System.out.println("Steer right!");
       }

       public void left() {

           System.out.println("Steer left!");
       }
   }

   public class Seat {

       public void up() {

           System.out.println("Seat up!");
       }

       public void down() {

           System.out.println("Seat down!");
       }
   }
}
הנה לנו הכיתה Bicycle. יש לו 2 שדות ושיטה אחת: start(). כיתות פנימיות מקוננות - 3הוא שונה ממחלקה רגילה בכך שהוא מכיל שתי מחלקות: Handlebarו Seat. הקוד שלהם כתוב בתוך Bicycleהכיתה. אלה הם שיעורים מן המניין: כפי שאתה יכול לראות, לכל אחד מהם יש שיטות משלו. בשלב זה, אולי יש לך שאלה: למה בכלל שנכניס מחלקה אחת לכיתה אחרת? למה לעשות להם מעמדות פנימיים? ובכן, נניח שאנחנו צריכים שיעורים נפרדים למושגים של כידון ומושב בתוכנית שלנו. כמובן, זה לא הכרחי עבורנו להפוך אותם לקנן! אנחנו יכולים לעשות שיעורים רגילים. לדוגמה, כך:
public class Handlebar {
   public void right() {
       System.out.println("Steer right!");
   }

   public void left() {

       System.out.println("Steer left");
   }
}

public class Seat {

   public void up() {

       System.out.println("Seat up!");
   }

   public void down() {

       System.out.println("Seat down!");
   }
}
שאלה טובה מאוד! כמובן, אנחנו לא מוגבלים על ידי הטכנולוגיה. לעשות את זה זו בהחלט אופציה. כאן, הדבר החשוב הוא יותר עיצוב נכון של השיעורים מנקודת מבט של תוכנית ספציפית ומטרתה. מחלקות פנימיות מיועדות להפרדת ישות המחוברת בל יינתק לישות אחרת. כידון, מושבים ודוושות הם מרכיבים של אופניים. כשהם מופרדים מהאופניים, הם לא הגיוניים במיוחד. אם היינו הופכים את כל המושגים האלה לשיעורים ציבוריים נפרדים, היה לנו את הקוד כזה בתוכנית שלנו:
public class Main {

   public static void main(String[] args) {
       Handlebar handlebar = new Handlebar();
       handlebar.right();
   }
}
הממ... את המשמעות של הקוד הזה אפילו קשה להסביר. יש לנו איזה כידון מעורפל (למה זה נחוץ? אין מושג, למען האמת). והידית הזו מסתובבת ימינה... לגמרי לבד, בלי אופניים... משום מה. על ידי הפרדת קונספט הכידון לקונספט האופניים, איבדנו קצת הגיון בתוכנית שלנו. באמצעות מחלקה פנימית, הקוד נראה שונה מאוד:
public class Main {

   public static void main(String[] args) {

       Bicycle peugeot = new Bicycle("Peugeot", 120);
       Bicycle.Handlebar handlebar = peugeot.new Handlebar();
       Bicycle.Seat seat = peugeot.new Seat();

       seat.up();
       peugeot.start();
       handlebar.left();
       handlebar.right();
   }
}
פלט מסוף:

Seat up! 
Let's go! 
Steer left! 
Steer right!
עכשיו מה שאנחנו רואים פתאום הגיוני! :) יצרנו חפץ לאופניים. יצרנו שני "תתי חפצים" לאופניים - כידון ומושב. הרמנו את המושב לנוחות ויצאנו לדרך: דיווש והיגוי לפי הצורך! :) השיטות שאנו צריכים נקראות על האובייקטים המתאימים. הכל פשוט ונוח. בדוגמה זו, הפרדת הכידון והמושב משפרת את המעטפת (אנחנו מסתירים נתונים על חלקי האופניים בתוך המחלקה הרלוונטית) ומאפשרת לנו ליצור הפשטה מפורטת יותר. עכשיו בואו נסתכל על מצב אחר. נניח שאנו רוצים ליצור תוכנית המדמה חנות אופניים וחלקי חילוף לאופניים. כיתות פנימיות מקוננות - 4במצב זה, הפתרון הקודם שלנו לא יהיה עבודה. בחנות אופניים, כל חלק אופניים בנפרד הגיוני גם כשהוא מופרד מאופניים. לדוגמה, נצטרך שיטות כמו "מכירת דוושות ללקוח", "קניית מושב חדש" וכו'. זו תהיה טעות להשתמש בשיעורים פנימיים כאן - לכל חלק אופניים בודד בתוכנית החדשה שלנו יש משמעות העומדת על משלו: ניתן להפריד אותו מהמושג אופניים. זה בדיוק מה שאתה צריך לשים לב אליו אם אתה תוהה אם אתה צריך להשתמש במחלקות פנימיות או לארגן את כל הישויות כשיעורים נפרדים. תכנות מונחה עצמים טוב בכך שהוא מקל על מודלים של ישויות בעולם האמיתי. זה יכול להיות העיקרון המנחה שלך כשאתה מחליט אם להשתמש בשיעורים פנימיים. בחנות אמיתית חלקי חילוף נפרדים מאופניים - זה בסדר. זה אומר שזה בסדר גם כשמעצבים תוכנית. אוקיי, הבנו את ה"פילוסופיה" :) עכשיו בואו נכיר מאפיינים "טכניים" חשובים של כיתות פנימיות. הנה מה שאתה בהחלט צריך לזכור ולהבין:
  1. אובייקט של מחלקה פנימית לא יכול להתקיים ללא אובייקט של מחלקה חיצונית.

    זה הגיוני: זו הסיבה שעשינו את השיעורים Seatוהפנימיות Handlebarבתוכנית שלנו - כדי שלא נצא עם כידון ומושבים יתומים.

    קוד זה אינו קומפילציה:

    public static void main(String[] args) {
    
       Handlebar handlebar = new Handlebar();
    }

    תכונה חשובה נוספת נובעת מכך:

  2. לאובייקט של מחלקה פנימית יש גישה למשתנים של המחלקה החיצונית.

    לדוגמה, בואו נוסיף int seatPostDiameterמשתנה (המייצג את קוטר עמוד המושב) לכיתה שלנו Bicycle.

    לאחר מכן, במחלקה Seatהפנימית, נוכל ליצור displaySeatProperties()שיטה המציגה את מאפייני המושב:

    public class Bicycle {
    
       private String model;
       private int weight;
    
       private int seatPostDiameter;
    
       public Bicycle(String model, int weight, int seatPostDiameter) {
           this.model = model;
           this.weight = weight;
           this.seatPostDiameter = seatPostDiameter;
    
       }
    
       public void start() {
           System.out.println("Let's go!");
       }
    
       public class Seat {
    
           public void up() {
    
               System.out.println("Seat up!");
           }
    
           public void down() {
    
               System.out.println("Seat down!");
           }
    
           public void displaySeatProperties() {
    
               System.out.println("Seat properties: seatpost diameter = " + Bicycle.this.seatPostDiameter);
           }
       }
    }

    ועכשיו אנחנו יכולים להציג את המידע הזה בתוכנית שלנו:

    public class Main {
    
       public static void main(String[] args) {
    
           Bicycle bicycle = new Bicycle("Peugeot", 120, 40);
           Bicycle.Seat seat = bicycle.new Seat();
    
           seat.displaySeatProperties();
       }
    }

    פלט מסוף:

    
    Seat properties: seatpost diameter = 40

    הערה:המשתנה החדש מוכרז עם שינוי הגישה המחמיר ביותר ( private). ועדיין למעמד הפנימי יש גישה!

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

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

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

    public static Seat createSeat() {
    
       // Bicycle.this cannot be referenced from a static context
       return new Seat();
    }
  4. מחלקה פנימית אינה יכולה להכיל משתנים ושיטות סטטיות.

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

    אבל ללא אובייקט של המעמד החיצוני, לא תהיה לנו גישה למעמד הפנימי.

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

    המהדר יפיק שגיאה אם ​​תנסה ליצור אותם:

    public class Bicycle {
    
       private int weight;
    
    
       public class Seat {
    
           // An inner class cannot have static declarations
           public static void displaySeatProperties() {
    
               System.out.println("Seat properties: seatpost diameter = " + Bicycle.this.seatPostDiameter);
           }
       }
    }
  5. בעת יצירת אובייקט של מחלקה פנימית, משנה הגישה שלו חשוב.

    ניתן לסמן מחלקה פנימית במתקני הגישה הסטנדרטיים: public, private, protected, ו package private.

    למה זה משנה?

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

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

    אגב, כבר עשינו את זה כאן:

    public class Main {
    
       public static void main(String[] args) {
    
           Bicycle peugeot = new Bicycle("Peugeot", 120);
           Bicycle.Handlebar handlebar = peugeot.new Handlebar();
           Bicycle.Seat seat = peugeot.new Seat();
    
           seat.up();
           peugeot.start();
           handlebar.left();
           handlebar.right();
       }
    }

    הגענו בקלות לכיתה Handlebarהפנימית מהכיתה Main.

    אם נכריז על המחלקה הפנימית כ- private, נוכל ליצור אובייקטים רק בתוך המחלקה החיצונית.

    אנחנו כבר לא יכולים ליצור Seatאובייקט "בחוץ":

    private class Seat {
    
       // Methods
    }
    
    public class Main {
    
       public static void main(String[] args) {
    
           Bicycle bicycle = new Bicycle("Peugeot", 120, 40);
    
           // Bicycle.Seat has private access in Bicycle
           Bicycle.Seat seat = bicycle.new Seat();
       }
    }

    בטח כבר הבנתם את ההיגיון :)

  6. משנה גישה למחלקות פנימיות פועלות כמו עבור משתנים רגילים.

    השינוי protectedמספק גישה למשתנה מופע בתתי מחלקות ומחלקות שנמצאות באותה חבילה.

    protectedעובד גם עבור כיתות פנימיות. אנו יכולים ליצור protectedאובייקטים של המעמד הפנימי:

    • במעמד החיצוני;
    • בתתי המחלקות שלו;
    • בשיעורים שנמצאים באותה חבילה.

    אם למחלקה הפנימית אין משנה גישה ( package private), ניתן ליצור אובייקטים של המחלקה הפנימית:

    • במעמד החיצוני;
    • בשיעורים שנמצאים באותה חבילה.

    אתה מכיר את השינויים כבר זמן רב, אז אין כאן בעיות.

זה הכל לבינתיים :) אבל אל תתרפי! שיעורים פנימיים הם נושא נרחב למדי שנמשיך לחקור בשיעור הבא. עכשיו אתה יכול לרענן את הזיכרון שלך מהשיעור של הקורס שלנו על כיתות פנימיות . ובפעם הבאה, בואו נדבר על מחלקות מקוננות סטטיות.
הערות
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION