JavaRush /בלוג Java /Random-HE /מחלקות, סוגי מחלקות מקוננות עם דוגמאות
Ярослав
רָמָה
Днепр

מחלקות, סוגי מחלקות מקוננות עם דוגמאות

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

מה זה כיתה?

מחלקה היא תיאור הגיוני של משהו, תבנית שבעזרתה ניתן ליצור מופעים אמיתיים של אותו דבר. במילים אחרות, זה פשוט תיאור של איך הישויות שנוצרו צריכות להיות: אילו תכונות ושיטות אמורות להיות להן. מאפיינים הם מאפיינים של ישות, שיטות הן פעולות שהיא יכולה לבצע. דוגמה טובה לכיתה מהחיים האמיתיים, שנותנת הבנה של מהי כיתה, יכולה להיחשב לשרטוטים: רישומים משמשים לתיאור מבנים (בליסט, מברג), אך ציור אינו עיצוב. בדיוק כפי שמהנדסים משתמשים בשרטוטים ליצירת עיצובים, התכנות משתמש במחלקות כדי ליצור אובייקטים שתיארו מאפיינים ושיטות.
public class Student {
    private String name, group, specialty;

    public Student(String name, String group, String specialty) {
       this.name = name;
       this.group = group;
       this.specialty = specialty;
   }

   // getters/setters
}
בדוגמה זו, יצרנו כיתת Java שמתארת ​​את הישות "תלמיד": לכל תלמיד יש שם, קבוצה והתמחות. כעת, במקומות אחרים בתוכנית, נוכל ליצור דוגמאות אמיתיות לשיעור הזה. במילים אחרות: אם הכיתה Studentהיא דיוקן של מה שתלמיד צריך להיות, אז המופע שנוצר הוא התלמיד עצמו. דוגמה ליצירת תלמיד חדש: new Student("Ivan", "KI-17-2", "Computer Engineering");האופרטור newמחפש את הכיתה Studentואז קורא למתודה מיוחדת (קונסטרוקטור) של הכיתה הזו. הקונסטרוקטור מחזיר חפץ כיתתי מוכן Student- תלמידנו היקר והרעב ללא מלגה :))

סוגי שיעורים ב-Java

ב-Java יש 4 סוגים של מחלקות בתוך מחלקה אחרת:
  1. מחלקות פנימיות מקוננות הן מחלקות לא סטטיות בתוך מחלקה חיצונית.

  2. מחלקות סטטיות מקוננות הן מחלקות סטטיות בתוך מחלקה חיצונית.

  3. מחלקות מקומיות של Java הן מחלקות בתוך שיטות.

  4. מחלקות ג'אווה אנונימיות הן מחלקות שנוצרות תוך כדי תנועה.

נדבר על כל אחד מהם בנפרד.

מחלקות לא סטטיות בתוך מחלקה חיצונית

ראשית, אני רוצה שתבינו מה זה עם דוגמה אמיתית, כי זה מקל בהרבה על ההבנה. אז עכשיו נפרק דבר גדול אמיתי לרכיבים קטנים יותר, ונפרק מטוס! עם זאת, לשם הדוגמה, די יהיה להראות מעט, לא נפרק את זה לגמרי. כדי לדמיין תהליך זה, נשתמש בתרשים מטוס. מחלקות, סוגי מחלקות מקוננות עם דוגמאות - 2 ראשית, עלינו ליצור מחלקה Airplaneשבה נוכל להוסיף תיאור קטן: שם מטוס, קוד זיהוי, טיסה.
public class Airplane {
    private String name, id, flight;

    public Airplane(String name, String id, String flight) {
        this.name = name;
        this.id = id;
        this.flight = flight;
    }

    // getters/setters
}
עכשיו אנחנו רוצים להוסיף כנפיים. ליצור כיתה נפרדת? אולי זה ההיגיון אם יש לנו תוכנית מורכבת לתכנון מטוסים, ושבה אנחנו צריכים ליצור מספר עצום של מחלקות נגזרות (מחלקות שיש להן אותו היגיון כמו מחלקת האב, כלומר המחלקה שממנה הם יורשים, אבל אז הם מרחיבים את מחלקת האב על ידי הוספת לוגיקה או מאפיינים מפורטים יותר), אבל מה אם יש לנו רק משחק שבו יש לנו מטוס אחד? אז זה יהיה יותר רציונלי עבורנו להשלים את כל המבנה במקום אחד (בכיתה אחת). כאן נכנסות לתמונה מחלקות מקוננות לא סטטיות. בעיקרו של דבר, זהו תיאור מפורט יותר של כמה פרטים של הכיתה החיצונית שלנו. בדוגמה זו, עלינו ליצור כנפיים למטוס - ימין ושמאל. בואו ליצור!
public class Airplane {
    private String name, id, flight;
    private Wing leftWing = new Wing("Red", "X3"), rightWing = new Wing("Blue", "X3");

    public Airplane(String name, String id, String flight) {
        this.name = name;
        this.id = id;
        this.flight = flight;
    }

    private class Wing {
        private String color, model;

        private Wing(String color, String model) {
            this.color = color;
            this.model = model;
        }

        // getters/setters
    }

    // getters/setters
}
אז יצרנו מחלקה מקוננת לא סטטית Wing(כנף) בתוך מחלקה Airplane(מטוס), והוספנו שני משתנים - כנף שמאל וכנף ימנית. ולכל כנף יש תכונות משלה (צבע, דגם) שאנו יכולים לשנות. כך תוכלו לאייש את המבנים ככל שתצטרכו. ושימו לב: קודם לכן בתרשים היו די הרבה חלקים למטוס, ולמעשה, אנחנו יכולים לחלק את כל החלקים למחלקות פנימיות, אבל תהליך כזה לא תמיד מומלץ. יש להתחקות אחר רגעים כאלה בהתאם למשימה. אולי לא תזדקק לכנפיים בכלל כדי לפתור את הבעיה. אז אין צורך לעשות אותם. זה כמו לחתוך אדם לרגליים, לזרועות, לגו ולראש - זה אפשרי, אבל למה אם המעמד הזה משמש רק לאחסון נתונים על אנשים? תכונות של מחלקות Java מקוננות לא סטטיות:
  1. הם קיימים רק באובייקטים, אז כדי ליצור אותם אתה צריך אובייקט. במילים אחרות: תכננו את הכנף שלנו להיות חלק ממטוס, אז כדי ליצור כנף אנחנו צריכים מטוס, אחרת אנחנו לא צריכים אותה.
  2. לא יכולים להיות משתנים סטטיים בתוך מחלקת Java. אם אתה צריך כמה קבועים או כל דבר סטטי אחר, אתה צריך להעביר אותם למחלקה חיצונית. זה נובע מהצימוד ההדוק של המחלקה המקוננת הלא סטטית למחלקה החיצונית.
  3. לכיתה יש גישה מלאה לכל השדות הפרטיים של הכיתה החיצונית. תכונה זו פועלת בשתי דרכים.
  4. אתה יכול לקבל הפניה למופע של מחלקה חיצונית. דוגמה: Airplane.זהו קישור למטוס, זה קישור לכנף.

מחלקות סטטיות בתוך מחלקה חיצונית

סוג זה של מחלקה אינו שונה ממחלקה חיצונית רגילה, למעט דבר אחד: כדי ליצור מופע של מחלקה כזו, אתה צריך לרשום את כל הנתיב מהמחלקה החיצונית למחלקה הרצויה, מופרד בנקודה. לדוגמה: Building.Plaftorm platform = new Building.Platform(); מחלקות סטטיות משמשות כדי לשים מחלקות קשורות זו לצד זו, כך שהמבנה הלוגי יהיה קל יותר לעבודה. לדוגמא: נוכל ליצור מחלקה חיצונית Building, שבה תהיה רשימה ספציפית של מחלקות שתייצג בניין ספציפי.
public abstract class Building {
    private String name, address, type;

    Building(String name, String address) {
        this.name = name;
        this.address = address;
    }

    public static class Platform extends Building {
        public Platform(String name, String address) {
            super(name, address);
            setType("Platform");
        }

        // some additional logic
    }

    public static class House extends Building {
        public House(String name, String address) {
            super(name, address);
            setType("House");
        }

        // some additional logic
    }

    public static class Shop extends Building {
        public Shop(String name, String address) {
            super(name, address);
            setType("Shop");
        }

        // some additional logic
    }

    // getters/setters
}
דוגמה זו מדגימה כיצד מחלקות סטטיות מאפשרות לך לארוז מבנה לוגי לצורה נוחה יותר. אם הם לא היו קיימים, היינו צריכים ליצור 4 מחלקות שונות לחלוטין. היתרונות של גישה זו:
  1. מספר הכיתות ירד.
  2. כל השיעורים נמצאים בתוך כיתת ההורים שלהם. אנו מסוגלים להתחקות אחר ההיררכיה כולה מבלי לפתוח כל מחלקה בנפרד.
  3. אנחנו יכולים להתייחס למחלקה Building, וה-IDE כבר יציג את כל הרשימה של כל תת המחלקות של המחלקה הזו. זה יקל על מציאת השיעורים שאתה צריך ולהראות את התמונה השלמה בצורה הוליסטית יותר.
דוגמה ליצירת מופע של מחלקה סטטית מקוננת:Building.Shop myShop = new Building.Shop(“Food & Fun!”, “Kalyaeva 8/53”); אני רוצה גם לציין שהאסטרטגיה הזו משמשת במחלקות AWT 2D לתיאור צורות, כגון Line2D, Arc2D, Ellipse2D ואחרות.

חוגים מקומיים

מחלקות אלו מוצהרות בתוך שיטות אחרות. למעשה, יש להם את כל המאפיינים של מחלקה מקוננת לא סטטית, רק את המופעים שלהם ניתן ליצור רק בשיטה, והמתודה לא יכולה להיות סטטית (כדי ליצור אותם צריך מופע של מחלקה חיצונית, הפניה ל- מופע של האובייקט הקורא מועבר באופן מרומז לשיטות לא סטטיות, ובשיטה סטטית אין שיטה לקישור זה). אבל יש להם מאפיינים משלהם:
  1. מחלקות מקומיות יכולות לעבוד רק עם משתני שיטה סופית. העניין הוא שניתן לאחסן מופעים של מחלקות מקומיות בערימה לאחר השלמת השיטה, וניתן למחוק את המשתנה. אם המשתנה מוכרז כסופי, אז המהדר יכול לשמור עותק של המשתנה לשימוש מאוחר יותר על ידי האובייקט. ועוד משהו: מאז 8+ גרסאות של Java, אתה יכול להשתמש במשתנים לא סופיים במחלקות מקומיות, אבל רק בתנאי שהם לא ישתנו.
  2. לא ניתן להצהיר על מחלקות מקומיות עם משנה גישה.
  3. למחלקות מקומיות יש גישה למשתני מתודה.
ניתן למצוא מחלקות מקומיות לעתים נדירות ביותר, מכיוון שהן מקשות על קריאת הקוד ואין להן יתרונות, מלבד אחד - גישה למשתני מתודה. אני לא יודע איזו דוגמה של כיתה מקומית אפשר לקחת שתראה את השימוש האפקטיבי שלה, אז אני רק אראה את הדוגמה שלי. נניח שיש לנו מחלקה Person(יש להניח שמדובר באדם) עם נכסים street(רחוב), house(בית). ברצוננו להחזיר חפץ כלשהו כדי לגשת רק למיקומו של האדם. לשם כך, יצרנו את ממשק AddressContainer, אשר מרמז על אחסון נתונים על מיקומו של אדם.
public class Person {
    private String name, street, house;

    public Person(String name, String street, String house) {
        this.name = name;
        this.street = street;
        this.house = house;
    }

    private interface AddressContainer {
        String getStreet();
        String getHouse();
    }

    public AddressContainer getAddressContainer() {
        class PersonAddressContainer implements AddressContainer {
            final String street = Person.this.street, house = Person.this.house;

            @Override
            public String getStreet() {
                return this.street;
            }

            @Override
            public String getHouse() {
                return this.house;
            }
        }

        return new PersonAddressContainer();
    }

    public static void main(String[] args) {
        Person person = new Person("Nikita", "Sholohova", "17");

        AddressContainer address = person.getAddressContainer();

        System.out.println("Address: street - " + address.getStreet() + ", house - " + address.getHouse());
    }

    // getters/setters
}
כפי שניתן לראות, בתוך השיטה יצרנו מחלקה שמיישמת אחסון של מיקומו של אדם, יצרנו שם משתנים קבועים (כך שלאחר היציאה מהשיטה נשמרו המשתנים באובייקט) והטמענו שיטה לקבלת הכתובת ו בַּיִת. כעת נוכל להשתמש באובייקט זה במקומות אחרים בתוכנית כדי לקבל את מיקומו של אדם. אני מבין שהדוגמה הזו לא אידיאלית, וזה יהיה נכון יותר לעשות את זה פשוט על ידי השארת גטרים בכיתה Person, עם זאת, היצירה של המחלקה הזו והשימוש האפשרי שלה הוצגו, ואז זה תלוי בך.

שיעורים אנונימיים

מתחת למכסה המנוע, מחלקות אנונימיות הן רק מחלקות מקוננות רגילות שאינן סטטיות. המוזרות שלהם היא קלות השימוש שלהם. אתה יכול לכתוב את הכיתה שלך ישירות בעת יצירת מופע של מחלקה אחרת.
public class Animal {
    public void meow() {
        System.out.println("Meow!");
    }

    public static void main(String[] args) {
        Animal anonTiger = new Animal() {
            @Override
            public void meow() {
                System.out.println("Raaar!");
            }
        };

        Animal notAnonTiger = new Animal().new Tiger();

        anonTiger.meow(); // будет выведено Raaar!
        notAnonTiger.meow(); // будет выведено Raaar!
    }

    private class Tiger extends Animal {
        @Override
        public void meow() {
            System.out.println("Raaar!");
        }
    }
}
בעיקרו של דבר, אנחנו פשוט משלבים שני דברים במקום אחד: יוצרים מופע של מחלקה אחת ( Animal) ויוצרים מופע של המחלקה הפנימית היורשת שלה ( Tiger). אחרת, עלינו ליצור את המחלקה בנפרד ולהשתמש במבנים ארוכים יותר כדי להשיג את אותה תוצאה. השימוש בשיעורים אנונימיים מוצדק במקרים רבים, במיוחד כאשר:
  • גוף הכיתה קצר מאוד;
  • יש צורך רק במופע אחד של המחלקה;
  • המחלקה משמשת במקום שבו נוצרה או מיד אחריה;
  • שם המחלקה אינו חשוב ואינו מקל על הבנת הקוד.
מחלקות אנונימיות משמשות לעתים קרובות ב-GUI ליצירת מטפלי אירועים. לדוגמה, כדי ליצור כפתור ולהגיב ללחיצה שלו:
JButton b2 = new JButton("Click");
b2.addActionListener(new ActionListener() {
    public void actionPerformed(ActionEvent e) {
        System.out.println("Кнопка нажата!");
    }
});
עם זאת, אחרי Java 8 התחילו להשתמש בביטויי למבדה, אבל עדיין הרבה קוד נכתב לפני גרסה 8 ואתה עלול להיתקל (ותיתקל במהלך האימון שלך ב-JavaRush) בכתובות כאלה.\ אנלוגי עם lambdas:
JButton b2 = new JButton("Click");
b2.addActionListener(e -> System.out.println("Кнопка нажата!"));
סוף המאמר תודה לכולכם על תשומת הלב ואני מקווה שלמדתם משהו חדש או הבנתם משהו שלא הבנתם קודם. אני גם רוצה להבהיר שמאמר זה שייך לקטגוריית "תשומת לב לפרטים" . זו העבודה הראשונה שלי, אז אני מקווה שזה היה שימושי למישהו. בזמן הקרוב כשיבואו רעיונות חדשים אנסה לכתוב משהו אחר, יש לי רק רעיון אחד... בהצלחה לכולם והצלחה בתכנות :)
הערות
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION