JavaRush /مدونة جافا /Random-AR /الطبقات الداخلية المتداخلة أو الفئة الداخلية في Java

الطبقات الداخلية المتداخلة أو الفئة الداخلية في Java

نشرت في المجموعة
مرحبًا! سنبدأ اليوم في النظر إلى موضوع مهم - كيفية عمل الفئات المتداخلة في Java. في اللغة الإنجليزية يطلق عليهم الطبقات المتداخلة. تتيح لك Java إنشاء بعض الفئات داخل فئات أخرى:
class OuterClass {
    ...
    static class StaticNestedClass {
        ...
    }
    class InnerClass {
        ...
    }
}
هذه الفئات هي التي تسمى متداخلة. وهي مقسمة إلى نوعين:
  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- دراجة. يحتوي على حقلين وطريقة واحدة - 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()تخبرنا بمعلمة المقعد:

    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و privateو protectedو package 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. تعمل معدّلات الوصول للفئات الداخلية بنفس الطريقة بالنسبة للمتغيرات العادية.

    يوفر المعدل protectedإمكانية الوصول إلى متغير فئة في فئاته التابعة وفي الفئات الموجودة في نفس الحزمة.

    وينطبق الشيء نفسه protectedعلى الطبقات الداخلية. يمكن إنشاء كائنات protectedالطبقة الداخلية :

    • داخل الطبقة "الخارجية"؛
    • في طبقاته المنحدرة؛
    • في تلك الفئات الموجودة في نفس الحزمة.

    إذا لم يكن للفئة الداخلية معدل وصول ( package private)، فيمكن إنشاء كائنات للفئة الداخلية

    • داخل الطبقة "الخارجية"؛
    • في الفئات الموجودة في نفس الحزمة.

    لقد كنت على دراية بالمعدلات لفترة طويلة، لذلك لن تكون هناك أي مشاكل هنا.

هذا كل شيء في الوقت الحالي :) لكن لا تسترخي! تعد الفصول المتداخلة الداخلية موضوعًا واسعًا إلى حد ما وسنستمر في استكشافه في الدروس المستقبلية. يمكنك الآن متابعة المحاضرة الخاصة بالفصول الداخلية من دورتنا. وفي المرة القادمة سنتحدث عن الفئات المتداخلة الثابتة.
تعليقات
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION