CodeGym /وبلاگ جاوا /Random-FA /کلاس های داخلی تو در تو
John Squirrels
مرحله
San Francisco

کلاس های داخلی تو در تو

در گروه منتشر شد
سلام! امروز به یک موضوع مهم می پردازیم - نحوه کار کلاس های تو در تو در جاوا. جاوا به شما امکان می دهد کلاس هایی را در کلاس دیگری ایجاد کنید:
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 فیلد و 1 روش: 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()متدی ایجاد کنیم که ویژگی‌های seat را نمایش دهد:

    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