JavaRush /وبلاگ جاوا /Random-FA /کلاس های داخلی تو در تو یا کلاس داخلی در جاوا

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

در گروه منتشر شد
سلام! امروز ما شروع به بررسی یک موضوع مهم خواهیم کرد - کلاس های تو در تو در جاوا چگونه کار می کنند. در زبان انگلیسی به آنها کلاس های تودرتو می گویند. جاوا به شما این امکان را می دهد که چند کلاس را در کلاس های دیگر ایجاد کنید:
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("Go!");
   }

   public class HandleBar {

       public void right() {
           System.out.println("Steering wheel to the right!");
       }

       public void left() {

           System.out.println("Steering wheel to the left!");
       }
   }

   public class Seat {

       public void up() {

           System.out.println("The seat is up!");
       }

       public void down() {

           System.out.println("The seat is down!");
       }
   }
}
اینجا ما یک کلاس داریم Bicycle- دوچرخه. دارای 2 فیلد و 1 روش - start(). کلاس های داخلی تو در تو - 3تفاوت آن با یک کلاس معمولی این است که دو کلاس دارد که کد آنها در داخل نوشته شده است Bicycle- این کلاس ها HandleBar(فرمان) و Seat(صندلی) هستند. اینها کلاس های کامل هستند: همانطور که می بینید، هر یک از آنها روش های خاص خود را دارند. در این مرحله، ممکن است این سوال برای شما پیش بیاید: چرا ما یک کلاس را در کلاس دیگر قرار دادیم؟ چرا آنها را داخلی کنیم؟ خوب، فرض کنید در برنامه به کلاس های جداگانه ای برای فرمان و صندلی نیاز داریم. اما شما مجبور نیستید آنها را لانه کنید! شما می توانید کلاس های منظم برگزار کنید. به عنوان مثال، مانند این:
public class HandleBar {
   public void right() {
       System.out.println("Steering wheel to the right!");
   }

   public void left() {

       System.out.println("Steering wheel left");
   }
}

public class Seat {

   public void up() {

       System.out.println("The seat is up!");
   }

   public void down() {

       System.out.println("The seat is 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();
   }
}
خروجی کنسول:

Сиденье поднято выше!
Поехали!
Руль влево!
Руль вправо!
اتفاقی که می افتاد ناگهان معنی پیدا کرد! :) ما یک شی دوچرخه ایجاد کرده ایم. ما دو مورد از "موضوع" آن را ایجاد کردیم - فرمان و صندلی. برای راحتی، صندلی را بالاتر بردیم - و رفتیم: به جایی که باید برویم غلت می زنیم و هدایت می کنیم! :) روش هایی که ما نیاز داریم بر روی اشیاء ضروری فراخوانی می شوند. همه چیز ساده و راحت است. در این مثال، برجسته کردن فرمان و صندلی، کپسولاسیون را افزایش می‌دهد (ما داده‌های مربوط به قسمت‌های دوچرخه را در کلاس مربوطه پنهان می‌کنیم)، و به ما امکان می‌دهد انتزاع دقیق‌تری ایجاد کنیم. حال بیایید وضعیت دیگری را بررسی کنیم. فرض کنید می خواهیم برنامه ای ایجاد کنیم که یک فروشگاه دوچرخه و قطعات را مدل سازی کند. کلاس های داخلی تو در تو - 4در این شرایط راه حل قبلی ما شکست خواهد خورد. در محدوده یک فروشگاه قطعات، هر قسمت از دوچرخه حتی جدا از ماهیت دوچرخه معنی دارد. به عنوان مثال، ما به روش هایی مانند «فروش پدال به خریدار»، «خرید صندلی جدید» و غیره نیاز خواهیم داشت. استفاده از کلاس‌های داخلی در اینجا اشتباه است - هر بخش جداگانه دوچرخه در برنامه جدید ما معنای خاص خود را دارد: از ماهیت دوچرخه جدا است و به هیچ وجه به آن وابسته نیست. این همان چیزی است که باید به آن توجه کنید اگر نمی‌خواهید از کلاس‌های داخلی استفاده کنید یا همه موجودیت‌ها را به کلاس‌های جداگانه جدا کنید. برنامه نویسی شی گرا عالی است زیرا مدل سازی موجودیت های دنیای واقعی را آسان می کند. این همان چیزی است که می توانید هنگام تصمیم گیری در مورد استفاده از کلاس های داخلی از آن به عنوان راهنما استفاده کنید. در یک فروشگاه واقعی، قطعات جدا از دوچرخه هستند - این طبیعی است. این به این معنی است که در هنگام طراحی یک برنامه درست خواهد بود. خوب، ما "فلسفه" را مرتب کردیم :) حالا بیایید با ویژگی های مهم "فنی" کلاس های داخلی آشنا شویم. در اینجا چیزی است که شما قطعا باید به خاطر بسپارید و درک کنید:
  1. یک شی از یک کلاس داخلی نمی تواند بدون یک شی از یک کلاس "بیرونی" وجود داشته باشد.

    این منطقی است: به همین دلیل است که ما آن را به کلاس های Seatداخلی تبدیل کردیم HandleBarتا فرمان و صندلی های بدون صاحب اینجا و آنجا در برنامه ما ظاهر نشوند.

    این کد کامپایل نمی شود:

    public static void main(String[] args) {
    
       HandleBar handleBar = new HandleBar();
    }

    ویژگی مهم زیر از این نتیجه می شود:

  2. یک شی از یک کلاس داخلی به متغیرهای کلاس "خارجی" دسترسی دارد.

    به عنوان مثال، بیایید Bicycleیک متغیر به کلاس خود اضافه کنیم int seatPostDiameter- قطر پایه صندلی.

    سپس در کلاس داخلی Seatمی توانیم متدی ایجاد کنیم getSeatParam()که پارامتر 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("Go!");
       }
    
       public class Seat {
    
           public void up() {
    
               System.out.println("The seat is up!");
           }
    
           public void down() {
    
               System.out.println("The seat is down!");
           }
    
           public void getSeatParam() {
    
               System.out.println("Seat parameter: 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.getSeatParam();
       }
    }

    خروجی کنسول:

    
    Параметр сиденья: диаметр подседельного штыря = 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 {
    
           //inner class cannot have static declarations
           public static void getSeatParam() {
    
               System.out.println("Seat parameter: seatpost diameter = " + Bicycle.this.seatPostDiameter);
           }
       }
    }
  5. هنگام ایجاد یک شی از یک کلاس داخلی، اصلاح کننده دسترسی آن نقش مهمی ایفا می کند.

    یک کلاس داخلی را می توان با اصلاح کننده های دسترسی استاندارد - public, و .privateprotectedpackage private

    چرا مهم است؟

    این تأثیر می گذارد که در برنامه ما می توانیم کلاس داخلی را نمونه سازی کنیم.

    اگر کلاس ما Seatبه صورت اعلان شود public، می توانیم اشیاء آن را در هر کلاس دیگری ایجاد کنیم. تنها شرط این است که شیء کلاس "خارجی" نیز باید وجود داشته باشد.

    به هر حال، ما قبلاً این کار را در اینجا انجام داده ایم:

    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 a private access in 'Bicycle'
           Bicycle.Seat seat = bicycle.new Seat();
       }
    }

    احتمالا از قبل منطق را فهمیده اید :)

  6. اصلاح کننده های دسترسی برای کلاس های داخلی مانند متغیرهای معمولی کار می کنند.

    Modifier protectedدسترسی به متغیر کلاس را در کلاس‌های فرعی آن و کلاس‌هایی که در همان بسته هستند فراهم می‌کند.

    protectedبرای طبقات داخلی هم همینطور است. اشیاء protectedکلاس داخلی را می توان ایجاد کرد:

    • در داخل کلاس "بیرونی"؛
    • در طبقات نسل خود؛
    • در کلاس هایی که در یک بسته هستند.

    اگر کلاس داخلی یک اصلاح کننده دسترسی ( ) نداشته باشد package private، می توان اشیاء کلاس داخلی ایجاد کرد

    • در داخل کلاس "بیرونی"؛
    • در کلاس هایی که در یک بسته هستند.

    شما مدت زیادی است که با اصلاح کننده ها آشنا هستید، بنابراین هیچ مشکلی در اینجا وجود نخواهد داشت.

فعلا فقط همین :) اما راحت نباش! کلاس های تو در تو داخلی موضوع نسبتاً گسترده ای است که در درس های آینده به بررسی آن ادامه خواهیم داد. اکنون می توانید سخنرانی در مورد کلاس های داخلی را از دوره ما بررسی کنید . و دفعه بعد در مورد کلاس های تو در تو ثابت صحبت خواهیم کرد.
نظرات
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION