القائمة الرئيسية

الصفحات

تعدد الاشكال والكلاسات المتداخلة في جافا

مفهوم الـ Polymorphism

Polymorphism تكتب بوليمورفيزم في اللغة العربية, و تعني تعدد الأشكال.
المقصود من البوليمورفيزم هو بناء دالة تنفذ أوامر مختلفة على حسب الكائن الذي يمرر لها كـ argument.
بوليمورفيزم تعني قدرة الكائن على أخذ عدة أشكال, تحديداً قدرة الكائن على التحول إلى نوع مشتق منه.


تحقيق البوليمورفيزم

البوليمورفيزم هو مجرد أسلوب في كتابة الكود و يمكنك تحقيقه بعدة أشكال مختلفة تجعلك تصل لفكرة تعدد الكائنات.
عملية البوليمورفيزم تبنى في العادة بداخل الدوال, حيث أننا نقوم ببناء دالة تأخذ باراميتر عبارة عن كلاس أو إنترفيس.
عند إستدعاء هذه الدالة, الكائن الذي نمرره لها كـ argument مكان هذا الباراميتر هو الذي يحدد طريقة عملها.

إذاً في البوليمورفيزم نستخدم مبدأ الـ Upcasting حيث أن الكائن الذي سيتم تمريره إلى الدالة يجب أن يحتوي على الدوال و الخصائص المشتركة بين الـ Superclass و الـ Subclass.
و عند إستدعاء أي دالة من الكائن الذي تم تمريره, ستكون هذه الدالة موجودة في الـ Superclass و الـ Subclass و لكنها ستتنفذ على أساس الـ Subclass.

لا تقلق ستفهم المقصود من الأمثلة.

Polymorphic Array

في دروس سابقة رأيت أنه يمكنك تعريف مصفوفة من أي نوع تريده, فمثلاً يمكنك تعريف مصفوفة من النوع int, double أو String إلخ..

  • في حال قمت بتعريف مصفوفة نوعها int, عندها يمكنك تخزين قيمة نوعها int في كل عنصر موجود فيها.

  • في حال قمت بتعريف مصفوفة نوعها String, عندها يمكنك تخزين قيمة نوعها String في كل عنصر موجود فيها.

خلاصة: في جافا, يمكنك تعريف مصفوفة من أي نوع تريده.

Polymorphic Array: تعني تطبيق مبدأ البوليمورفيزم من خلال مصفوفة.
و المقصود هنا أنه يمكنك تعريف مصفوفة نوعها كائن و تخزين كائنات من نفس نوعها فيها.


في المثال التالي قمنا بإنشاء كلاس إسمه A يحتوي على متغير إسمه x و دالة إسمها printX().
بعدها قمنا بإنشاء كلاس آخر إسمه Main.
بداخل الكلاس Main قمنا بإنشاء 5 كائنات من الكلاس A, ثم قمنا بتعريف مصفوفة إسمها list نوعها a و وضعنا فيها الكائنات التي أنشأناها من الكلاس A.

كل كائن وضعناه فيها يعتبر عنصر من عناصرها, و بالتالي يمكننا الوصول لهم أيضاً منها من خلال أرقام الـ index إن أردنا.
مثال: list[0], list[1], list[2] إلخ..

المثال الأول

A.java
	  public class A {           // A هنا قمنا بتعريف كلاس إسمه

	  public int x;

	  public void printX(){
	  System.out.println("x contain: " + x);
	  }

	  }
	

Main.java
	  public class Main {

	  public static void main(String[] args) {

	  // A أي 5 كائنات نوعهم ,A هنا قمنا بتعريف 5 كائنات من الكلاس
	  A a1 = new A();
	  A a2 = new A();
	  A a3 = new A();
	  A a4 = new A();
	  A a5 = new A();

	  // فيها A ثم قمنا بتخزين جميع الكائنات المشتقة من ,A هنا قمنا بإنشاء مصفوفة نوعها
	  A[] list = { a1, a2, a3, a4, a5 };

	  // list المخزن كثالث عنصر في المصفوفة a3 الموجود في الكائن x هنا قمنا بإعطاء قيمة للمتغير
	  list[2].x = 14;

	  // list[2] الموجودة في العنصر x سترجع قيمة المتغير printX() لاحظ أن الدالة
	  a3.printX();

	  // a3 الموجودة في العنصر x سترجع قيمة المتغير printX() و لاحظ أن الدالة
	  list[2].printX();

	  }

	  }
	

سنحصل على النتيجة التالية عند التشغيل.

	  x contain: 14
	  x contain: 14
	


إذاً يمكنك بناء مصفوفة نوعها كلاس, و تخزين كائنات من نفس النوع فيها كما رأيت في المثال السابق.
في المثال التالي سترى أنه يمكنك تخزين كائنات مشتقة من نفس نوع المصفوفة فيها أيضاً و هذا من مبادئ الـ Upcasting و تطبيق لمبدأ الـ Polymorphic Array.


في المثال التالي قمنا بإنشاء كلاس إسمه A و نوعه abstract, و يحتوي على دالة إسمها print() و نوعها abstract أيضاً.
بعدها قمنا بإنشاء إثنين كلاس B و C يرثان من A. بعدها قمنا بإنشاء كلاس آخر إسمه Main و الذي سنطبق فيه مبدأ الـ Polymorphic Array.
بداخل الكلاس Main قمنا بإنشاء كائن من الكلاس B و كائن من الكلاس C, ثم قمنا بتعريف مصفوفة إسمها list و نوعها a, و وضعنا فيها الكائنات التي ترث من الكلاس A.

المثال الثاني

A.java
	  public abstract class A {                  // إذاً لا يمكن إنشاء كائنات منه ,Abstract نوعه A الكلاس

	  public abstract void print();          // abstract لهذه الدالة لأن نوعها Override يجب أن يفعل A أي كلاس سيرث من الكلاس

	  }
	

B.java
	  public class B extends A {                 // A يرث من الكلاس B هنا قلنا أن الكلاس

	  @Override
	  public void print() {
	  System.out.println("class B");     // إذا قمت باستدعاء هذه الدالة من هذا الكلاس ستعرض هذه الجملة
	  }

	  }
	

C.java
	  public class C extends A {                 // A يرث من الكلاس C هنا قلنا أن الكلاس

	  @Override
	  public void print() {
	  System.out.println("class C");     // إذا قمت باستدعاء هذه الدالة من هذا الكلاس ستعرض هذه الجملة
	  }

	  }
	

Main.java
	  public class Main {

	  public static void main(String[] args) {

	  A b = new B();                         // B ثم قمنا بتحديد نوعه ككائن من A هنا قمنا بإنشاء كائن من الكلاس
	  A c = new C();                         // C ثم قمنا بتحديد نوعه ككائن من A هنا قمنا بإنشاء كائن من الكلاس

	  A[] list = new A[2];                   // تتألف من عنصرين فقط A هنا قمنا بإنشاء مصفوفة نوعها

	  // فيها A سنخزن الكائنات المشتقة من الكلاس
	  list[0] = b;                           // list[0] في أول عنصر في المصفوفة b هنا قمنا بتخزين الكائن
	  list[1] = c;                           // list[1] في ثاني عنصر في المصفوفة c هنا قمنا بتخزين الكائن

	  for(int i=0; i<list.length; i++) {     // هنا قمنا ببناء حلقة للمرور على جميع عناصر المصفوفة
	  list[i].print();                   // الخاصة بكل عنصر print() هنا سيتم إستدعاء الدالة
	  }                                      // على إختلاف كل كائن موجود في المصفوفة print() إذاً هنا سيختلف أداء الدالة

	  }

	  }
	

سنحصل على النتيجة التالية عند التشغيل.

	  class B
	  class C
	

Polymorphic Argument

Polymorphic Argument تعني بناء دالة تنفذ أوامر مختلفة على حسب الكائن الذي يمرر لها كـ argument.


الآن سنقوم بتعريف كلاس إسمه Shape و نوعه abstract, هذا الكلاس سنعتبره كلاس أساسي لجميع الأشكال الهندسية, أي سيكون Superclass لأي كلاس يمثل أحد الأشكال الهندسية. Shape يحتوي على دالة إسمها shapeForm() و نوعها abstract أيضاً, هذه الدالة فكرتها طباعة الشكل الهندسي للكائن الذي يقوم باستدعائها.

بعدها سنقوم بتعريف أربع كلاسات Square, Rectangle, Triangle و Circle.
كل كلاس منهم سيمثل شكل هندسي معين, إذاً يجب أن يرثوا جميعاً من الكلاس Shape, و يجب أن يفعلوا Override للدالة shapeForm() بهدف جعلها ترسم الشكل المطلوب من كل كلاس.

بعدها سنقوم بتعريف كلاس إسمه Drawer و الفكرة منه بناء كلاس لرسم أي كائن مشتق من Shape.

بعد إنشاء جميع هذه الكلاسات, سنقوم بإنشاء الكلاس Main لتجربة الكود.

مثال

Shape.java
	  public abstract class Shape {

	  // لها حتى تطبع الشكل المناسب له Override على كل كائن يمثل شكل هندسي أن يفعل
	  public abstract void shapeForm();

	  }
	

Square.java
	  public class Square extends Shape {

	  // إذا قمت باستدعاء هذه الدالة من هذا الكائن, ستعرض لك شكل يشبه المربع
	  @Override
	  public void shapeForm() {
	  System.out.println("* * * *\n* * * *\n* * * *\n");
	  }

	  }
	

Rectangle.java
	  public class Rectangle extends Shape {

	  // إذا قمت باستدعاء هذه الدالة من هذا الكائن, ستعرض لك شكل يشبه المربع
	  @Override
	  public void shapeForm() {
	  System.out.println("* * * * * *\n* * * * * *\n* * * * * *\n");
	  }

	  }
	

Triangle.java
	  public class Triangle extends Shape {

	  // إذا قمت باستدعاء هذه الدالة من هذا الكائن, ستعرض لك شكل يشبه المثلث
	  @Override
	  public void shapeForm() {
	  System.out.println("    *\n   * *\n  * * *\n * * * *\n* * * * *\n");
	  }

	  }
	

Circle.java
	  public class Circle extends Shape {

	  // إذا قمت باستدعاء هذه الدالة من هذا الكائن, ستعرض لك شكل يشبه الدائرة
	  @Override
	  public void shapeForm() {
	  System.out.println("  * * *\n* * * * *\n* * * * *\n  * * *\n");
	  }

	  }
	

Drawer.java
	  public class Drawer {

	  public void draw(Shape s) {       // Shape عند إستدعاء هذه الدالة يجب ان نمرر لها أي كائن مشتق من الكلاس
	  s.shapeForm();                // الموجودة في الكائن الذي قام باستدعاءها shapeForm() هنا سيتم استدعاء الدالة
	  }

	  }
	

Main.java
	  public class Main {

	  public static void main(String[] args) {

	  // هنا كل كائن سيمثل شكل هندسي معين <-- Shape هنا قمنا بإنشاء 4 كائنات من الكلاس
	  Shape s = new Square();           // Square و حددنا أن نوعه Shape هنا قمنا بإنشاء كائن من
	  Shape r = new Rectangle();        // Rectangle و حددنا أن نوعه Shape هنا قمنا بإنشاء كائن من
	  Shape t = new Triangle();         // Triangle و حددنا أن نوعه Shape هنا قمنا بإنشاء كائن من
	  Shape c = new Circle();           // Circle و حددنا أن نوعه Shape هنا قمنا بإنشاء كائن من

	  Drawer drawer = new Drawer();     // الموجودة فيه draw() حتى نستطيع استخدام الدالة drawer هنا قمنا بإنشاء كائن من

	  // لعرض شكل كل كائن draw() هنا قمنا باستدعاء الدالة
	  drawer.draw(s);                   // Square المعرفة في الكلاس shapeForm() سيتم إستدعاء الدالة ,Square نوعه s بما أن الكائن
	  drawer.draw(r);                   // Rectangle المعرفة في الكلاس shapeForm() سيتم إستدعاء الدالة ,Rectangle نوعه r بما أن الكائن
	  drawer.draw(t);                   // Triangle المعرفة في الكلاس shapeForm() سيتم إستدعاء الدالة ,Triangle نوعه t بما أن الكائن
	  drawer.draw(c);                   // Circle المعرفة في الكلاس shapeForm() سيتم إستدعاء الدالة ,Circle نوعه c بما أن الكائن

	  }

	  }
	

سنحصل على النتيجة التالية عند التشغيل.

	  * * * *
	  * * * *
	  * * * *

	  * * * * * *
	  * * * * * *
	  * * * * * *

	  *
	  * *
	  * * *
	  * * * *
	  * * * * *

	  * * *
	  * * * * *
	  * * * * *
	  * * *
	

العامل instanceof في Polymorphism

العامل instanceof يستخدم لمعرفة إذا كان الكائن مشتقاً من كلاس معين أم لا.
في حال كان مشتقاً منه فإنه يرجع true, أما إذا لم يكن مشتقاً منه فإنه يرجع false.


في المثال التالي قمنا بإنشاء كلاس إسمه A.
بعدها قمنا بإنشاء إثنين كلاس B و C يرثان من A.
بعدها قمنا بإنشاء كلاس آخر إسمه Main.
بداخل الكلاس Main قمنا بتعريف دالة إسمها check() تأخذ كائن مشتق من A كـ argument فتعرض لنا إسم الكلاس الذي إشتق منه الكائن.
بعدها قمنا بإنشاء كائن من الكلاس B و كائن من الكلاس C, ثم قمنا بتمريرهما في الدالة check().

مثال

A.java
	  public class A {

	  }
	

B.java
	  public class B extends A {

	  }
	

C.java
	  public class C extends A {

	  }
	

Main.java
	  public class Main {

	  public static void main(String[] args) {

	  A b = new B();         // B و حددنا أن نوعه A هنا قمنا بإنشاء كائن من
	  A c = new C();         // C و حددنا أن نوعه A هنا قمنا بإنشاء كائن من

	  check(b);              // لمعرفة إسم الكلاس المشتق منه check() في الدالة b هنا قمنا بتمرير الكائن
	  check(c);              // لمعرفة إسم الكلاس المشتق منه check() في الدالة c هنا قمنا بتمرير الكائن

	  }


	  // سنستخدمها لمعرفة إسم الكلاس الذي إشتق منه كل كائن static هنا قمنا بتعريف دالة نوعها
	  static void check (A obj) {
	  if(obj instanceof B)            // سيتم تنفيذ أمر الطباعة الموجود فيها B كائن من الكلاس obj في حال كان الـ
	  {
	  System.out.println("This is an object from the class B");
	  }
	  else if(obj instanceof C)       // سيتم تنفيذ أمر الطباعة الموجود فيها C كائن من الكلاس obj في حال كان الـ
	  {
	  System.out.println("This is an object from the class C");
	  }
	  }

	  }
	

سنحصل على النتيجة التالية عند التشغيل.

	  This is an object from the class B
	  This is an object from the class C 
	


إذاً هناك طرق كثيرة لتحقيق مبدأ تعدد الأشكال و قد تعلمنا بعضها في هذا الدرس.

مفهوم الـ Nested Classes في جافا

جافا تسمح لك بتعريف العدد الذي تريده من المتغيرات و الدوال بداخل الكلاس كما أنها تسمح لك بتعريف كلاس بداخل نفس الكلاس.
إذاً Nested Classes تعني كلاسات متداخلة, أي تعريف كلاس بداخل كلاس آخر.


هنا قمنا بتعريف كلاس إسمه A يحتوي على كلاس إسمه B.

المثال الأول

A.java
class A {

	  class B {

	  }

	  } 
	


الكلاس مهما كان نوعه يمكنه أن يحتوي على متغيرات و دوال و كلاسات متداخلة.
هنا قمنا بتعريف كلاس إسمه A يحتوي على متغير إسمه x, دالة إسمها print(), و كلاس إسمه B.

المثال الثاني

A.java
class A {

	  int x;

	  public void print() {
	  System.out.println("class A");
	  }

	  class B {

	  }

	  } 
	


يمكنك وضع العدد الذي تريده من الكلاسات المتداخلة بداخل بعض.
هنا قمنا بتعريف كلاس إسمه A يحتوي على إثنين كلاس و هما B و C.
C يحتوي أيضاً على كلاس إسمه D.

المثال الثالث

A.java
class A {

	  class B {

	  }

	  class C {
	  class D {
	  }
	  }

	  } 
	

فوائد الـ Nested Classes في جافا

  • طريقة يمكن اتباعها لتصنيف الكلاسات ضمن مجموعات بشكل منطقي.
    فمثلاً في حال كان الكلاس مصمم خصيصاً لكلاس آخر, فإنه من المنطق تعريف هذين الكلاسات مع بعضهما في نفس المجموعة.

  • من أجل تغليف البيانات Encapsulation بشكل أقوى. حيث أنه يمكنك تعريف الكلاس الداخلي كـ private و جعله فقط متاحاً أمام الكلاس الذي تم تعريفه بداخله.

  • لتسهيل معرفة الفائدة من الكلاس. فالـ Outer Class في العادة يتم إعطائه إسم يشير إلى الفائدة من أي كلاس موجود بداخله.

أنواع الـ Nested Classes في جافا

الـ Nested Classes ينقسمون إلى نوعين أساسيين و هما:

  • Static Nested Classes: و هي الكلاسات المتداخلة المعرفة كـ static.

  • Non Static Nested Classes: و هي الكلاسات المتداخلة الغير المعرفة كـ static.


مصطلحات تقنية

  • الكلاس الذي يحتوي على كلاس أو أكثر يسمى Outer Class.

  • الكلاس الموجود بداخل كلاس آخر يسمى Nested Class إذا كان نوعه static.

  • الكلاس الموجود بداخل كلاس آخر و الغير معرف كـ static يسمى Inner Class.
        و لا يهم إن كان معرفاً كـ public, private, protected أو package private.



أمثلة

هنا قمنا بتطبيق مبدأ الـ Static Nested Classes.

المثال الأول

public class A {             // B بالنسبة للكلاسات Outer Class يعتبر A الكلاس

	  static class B {         // static لأنه معرف كـ A بالنسبة للكلاس Nested Class يعتبر B الكلاس

	  }

	  } 
	

هنا قمنا بتطبيق مبدأ الـ Non Static Nested Classes.

المثال الثاني

public class A {             // F و D ,C ,B بالنسبة للكلاسات Outer Class يعتبر A الكلاس

	  public class B {         // static لأنه غير معرف كـ A فقط بالنسبة للكلاس Inner Class يعتبر B الكلاس

	  }

	  private class C {        // static لأنه غير معرف كـ A فقط بالنسبة للكلاس Inner Class يعتبر C الكلاس

	  }

	  protected class D {      // static لأنه غير معرف كـ A فقط بالنسبة للكلاس Inner Class يعتبر D الكلاس

	  }

	  class E {                // static لأنه غير معرف كـ A فقط بالنسبة للكلاس Inner Class يعتبر E الكلاس

	  }

	  }
	

التعامل مع الـ Static Nested Classes في جافا

Static Nested Class أو Nested Class عبارة عن كلاس يمكن الوصول له مباشرةً من الـ Outer Class دون حاجة إلى إنشاء كائن منه.
و يمكنه إحتواء متغيرات, دوال, و كلاسات أخرى و التي بدورها يمكن أن تكون معرفة كـ static أيضاً.
الأشياء المعرفة فيه كـ static, يمكن الوصول لها مباشرةً منه.
بينما الأشياء الغير المعرفة فيه كـ static, يمكن الوصول لها فقط من خلال إنشاء كائن منه.


مثال

A.java
public class A {                    // A إسمه Outer Class هنا قمنا بتعريف

	  static class B {                // B إسمه Nested Class هنا قمنا بتعريف

	  public int x;               // static لأنه غير معرف كـ B هذا المتغير يمكن الوصول له فقط من خلال كائن من الكلاس
	  public static int y;        // static لأنه معرف كـ A.B.y هذا المتغير يمكن الوصول له مباشرةً هكذا

	  public void printX() {                // static لأنها غير معرفة كـ B هذه الدالة يمكن الوصول لها فقط من خلال كائن من الكلاس
	  System.out.println("x contain: " + x);
	  }

	  public static void printY() {         // static لأنها معرفة كـ A.B.printY() هذه الدالة يمكن الوصول لها مباشرةً هكذا
	  System.out.println("y contain: " + y);
	  }

	  }

	  }
	

Main.java
public class Main {

	  public static void main(String[] args) {

	  A.B obj = new A.B();     // obj إسمه A الموجود بداخل الكلاس B هنا قمنا بإنشاء كائن من الكلاس

	  obj.x = 10;              // obj الموجود في الكائن x هنا قمنا بإعطاء قيمة للمتغير
	  obj.printX();            // obj من خلال الكائن printX() هنا قمنا باستدعاء الدالة

	  obj.y = 20;              // لا تستخدم هذا الأسلوب  <---  obj من خلال الكائن y هنا قمنا بإعطاء قيمة للمتغير
	  obj.printY();            // لا تستخدم هذا الأسلوب  <---  obj من خلال الكائن printY() هنا قمنا باستدعاء الدالة

	  A.B.y = 30;              // B بشكل مباشر من الكلاس y هنا قمنا بإعطاء قيمة للمتغير
	  A.B.printY();            // B بشكل مباشر من الكلاس printY() هنا قمنا باستدعاء الدالة

	  }

	  }
	

سنحصل على النتيجة التالية عند التشغيل.

x contain: 10
	  y contain: 20
	  y contain: 30 
	


إذا أردت الوصول لمحتويات كلاس نوعه static يفضل الوصول لهم من الكلاس مباشرةً و ليس من خلال كائن من الكلاس الذي يحتويهم.
إذا عدنا للكود السابق, في الكلاس Main, في السطرين 12 و 13 تحديدياً, ننصحك بالتالي:

  1. إعتماد A.B.y بدل obj.y.

  2. إعتماد A.B.printY() بدل obj.printY().

يفضل إتباع هذه النصائح دائماً لجعل برامجك أفضل من ناحية الأداء و السرعة.


كان بإمكانك أيضاً إنشاء الكائن obj بطريقة أخرى.

شاهد المثال »

التعامل مع الـ Non Static Nested Classes في جافا

Non Static Nested Class أو Inner Class عبارة عن كلاس يمكن الوصول له و لمحتوياته فقط من خلال كائن.
و يمكنه إحتواء متغيرات, دوال, و كلاسات أخرى أيضاً.
لا يمكن تعريف أي شيء فيه كـ static لأنه لا يمكن الوصول له إلا من خلال كائن.


أنواع الـ Inner Classes

الـ Inner Classes ينقسمون إلى ثلاث أنواع أساسية على حسب طريقة تعريفك لهم:

  • Inner Classes

  • Method Local Inner Classes

  • Anonymous Inner Classes

التعامل مع الـ Inner Classes في جافا

الـ Inner Class عبارة عن كلاس معرف بداخل كلاس.
الـ Inner Class يمكن أن يكون معرف كـ public أو private أو protected أو package private.
الـ Modifier الذي تستخدمه عند تعريف الكلاس يحدد الطرق التي يمكنك من خلالها الوصول لهذا الكلاس.


مثال

لنفترض أنه عندنا OuterClass إسمه a بداخله InnerClass إسمه B.

class A {         // OuterClass

	  class B {     // InnerClass

	  }

	  } 
	

هكذا ننشئ كائن من الكلاس B.

A a   = new A();       // OuterClass أولاً: ننشئ كائن من الـ
	  A.B b = a.new B();     // InnerClass ثانياً: ننشئ كائن من الـ
	

و يمكنك إنشاء كائن من الكلاس B بأمر واحد.

A.B b = new A().new B();
	

أمثلة شاملة

هنا قمنا بتعريف الـ Inner Class كـ public.

شاهد المثال »


هنا قمنا بتعريف الـ Inner Class كـ private.

شاهد المثال »

التعامل مع الـ Method Local Inner Classes في جافا

في جافا, يمكنك تعريف كلاس جديد بداخل دالة, و عندها يعتبر هذا الكلاس local Class فيها. بمعنى أنه يمكن الوصول له فقط من خلالها, حيث أنه يمكن إنشاء كائن من هذا الكلاس بداخلها فقط.


في المثال التالي قمنا بتعريف Outer Class إسمه a, يحتوي على دالة إسمها displayInnerClass().
الدالة displayInnerClass() تحتوي على كلاس إسمه B, و الذي بدوره يحتوي على دالة إسمها print().
بعد تعريف الكلاس B قمنا بإنشاء كائن منه لإستدعاء الدالة print() في داخل الدالة displayInnerClass().

إذاً الفكرة هنا هي تعريف كلاس و إنشاء كائن منه بداخل دالة.

مثال

A.java
public class A {                            // A إسمه Outer Class هنا قمنا بتعريف

	  // هنا قمنا بتعريف دالة تنشئ كلاس يحتوي على دالة أيضاً. ثم تنشئ كائن من الكلاس و تستدعي الدالة الموجودة فيه
	  public void displayInnerClass() {

	  class B {                           // B هنا قمنا بتعريف كلاس إسمه
	  void print() {                  // print() يحتوي على الدالة B
	  System.out.println("B is a Local Inner Class");
	  }
	  }

	  B b = new B();                      // B هنا قمنا بإنشاء كائن من الكلاس
	  b.print();                          // B الموجودة في الكلاس print() هنا قمنا باستدعاء الدالة
	  }

	  }
	

Main.java
public class Main {

	  public static void main(String[] args) {

	  // منه displayInnerClass() ثم قمنا باستدعاء الدالة A هنا قمنا بإنشاء كائن من الكلاس
	  A a = new A();
	  a.displayInnerClass();

	  }

	  }
	

سنحصل على النتيجة التالية عند التشغيل.

B is a Local Inner Class
	

التعامل مع الـ Anonymous Inner Classes في جافا

في حال كنت تريد إستخدام إنترفيس بدون أن تنشئ كلاس و تجعله ينفّذه, أو كنت تريد إستخدام كلاس نوعه abstract بدون أن تنشئ كلاس يرث منه يمكنك الإستفادة من أسلوب الـ Anonymous Inner Class.

إذاً فكرة الـ Anonymous Inner Class هي إنشاء كائن يحتوي على الأشياء الموجودة في كلاس ما بدون أن يفعل له extends, أو إنشاء كائن يحتوي على الأشياء الموجودة في إنترفيس ما بدون أن يفعل له implements.

لا تنسى أنه عند إعتماد هذا الأسلوب فإنك لا زلت مجبراً على أن تفعل Override لكل دالة نوعها abstract.


طريقة تعريف Anonymous Class

في البداية عليك أن تفعل import للكلاس أو الإنترفيس الذي تنوي إنشاء كائن منه في حال كنت تستخدم كلاس جاهز من الكلاسات الموجودة في جافا.
لتعريف Anonymous Class, عليك إنشاء كائن من كلاس معين, ثم فتح أقواس البداية و النهاية بعد الكونستركتور, و تعريف الـ Anonymous Class بداخله.


أمثلة شاملة

هنا وضعنا أمثلة مهمة تعلمك طريقة التعامل و الإستفادة من الـ Anonymous Class.

شاهد الأمثلة »


هنا وضعنا مثال طبقنا فيه مفهوم الـ Anonymous Class و مثال طبقنا فيه مفهوم الـ Anonymous Object.

شاهد المثال »

 مثال يعلمك طريقة أخرى لإنشاء كائن من Inner Class في جافا

في هذا المثال سنفعل import للكلاس B, ثم سننشئ منه كائن إسمه obj.
الكلاس A لم نغير أي شيء فيه, قمنا بتعليم الكود الذي أضفناه أو عدلنا فيه في الكلاس B باللون الأصفر.

A.java
                    public class A {                    // A إسمه Outer Class هنا قمنا بتعريف

	  static class B {                // B إسمه Nested Class هنا قمنا بتعريف

	  public int x;               // static لأنه غير معرف كـ B هذا المتغير يمكن الوصول له فقط من خلال كائن من الكلاس
	  public static int y;        // static لأنه معرف كـ A.B.y هذا المتغير يمكن الوصول له مباشرةً هكذا

	  public void printX() {                // static لأنها غير معرفة كـ B هذه الدالة يمكن الوصول له فقط من خلال كائن من الكلاس
	  System.out.println("x contain: " + x);
	  }

	  public static void printY() {         // static لأنها معرفة كـ A.B.printY() هذه الدالة يمكن الوصول لها مباشرةً هكذا
	  System.out.println("y contain: " + y);
	  }

	  }

	  }
	

Main.java
                    import A.B;                      // B للكلاس import هنا فعلنا

	  public class Main {

	  public static void main(String[] args) {

	  B obj = new B();         // obj إسمه A الموجود بداخل الكلاس B هنا قمنا بإنشاء كائن من الكلاس

	  obj.x = 10;              // obj الموجود في الكائن x هنا قمنا بإعطاء قيمة للمتغير
	  obj.printX();            // obj من خلال الكائن printX() هنا قمنا باستدعاء الدالة

	  obj.y = 20;              // لا تستخدم هذا الأسلوب  <---  obj من خلال الكائن y هنا قمنا بإعطاء قيمة للمتغير
	  obj.printY();            // لا تستخدم هذا الأسلوب  <---  obj من خلال الكائن printY() هنا قمنا باستدعاء الدالة

	  A.B.y = 30;              // B بشكل مباشر من الكلاس y هنا قمنا بإعطاء قيمة للمتغير
	  A.B.printY();            // B بشكل مباشر من الكلاس printY() هنا قمنا باستدعاء الدالة

	  }

	  }
	

سنحصل على النتيجة التالية عند التشغيل.

                    x contain: 10
	  y contain: 20
	  y contain: 30 
	

 مثال حول التعامل مع Public Inner Class في جافا

A.java
                    public class A {                    // A إسمه Outer Class هنا قمنا بتعريف

	  public class B {                // B إسمه Nested Class هنا قمنا بتعريف

	  public void print() {       // B هذه الدالة يمكن الوصول لها فقط من خلال كائن من الكلاس
	  System.out.println("B is a public inner class");
	  }

	  }

	  }
	

Main.java
                    public class Main {

	  public static void main(String[] args) {

	  A.B obj = new A().new B();         // obj إسمه A الموجود بداخل الكلاس B هنا قمنا بإنشاء كائن من الكلاس

	  obj.print();                       // obj من خلال الكائن print() هنا قمنا باستدعاء الدالة

	  }

	  }
	

سنحصل على النتيجة التالية عند التشغيل.

                    B is a public inner class
	

 مثال حول التعامل مع Private Inner Class في جافا

A.java
                    public class A {                    // A إسمه Outer Class هنا قمنا بتعريف

	  private class B {               // B إسمه Nested Class هنا قمنا بتعريف

	  public void print() {       // B هذه الدالة يمكن الوصول لها فقط من خلال كائن من الكلاس
	  System.out.println("B is a private inner class");
	  }

	  }

	  public void callPrintB() {      // public لأنها معرفة فيه كـ A هذه الدالة يمكن الوصول لها من كائن من الكلاس
	  B b = new B();              // b إسمه B هنا قمنا بإنشاء كائن من الكلاس
	  b.print();                  // b من الكائن print() هنا قمنا باستدعاء الدالة
	  }

	  }
	

Main.java
                    public class Main {

	  public static void main(String[] args) {

	  A obj = new A();          // A هنا قمنا بإنشاء كائن من الكلاس

	  obj.callPrintB();         // print() و التي ستستدعي بدورها الدالة callPrintB() هنا قمنا باستدعاء الدالة

	  }

	  }
	

سنحصل على النتيجة التالية عند التشغيل.

                    B is a private inner class
	

 أمثلة تشرح فكرة الـ Anonymous Inner Class في جافا

في المثال التالي سنقوم بإنشاء Anonymous Class يمكن الإستفادة منه كلما إحتجنا إليه لأننا قمنا بتخزينه في كائن من نفس النوع.
لنفترض أننا نريد تعريف كائن من كلاس إسمه Anonymous, مع تخزين هذا الكائن في كائن من نفس النوع إسمه obj.

المثال الأول

Anonymous.java
                    public abstract class Anonymous {       // Anonymous إسمه Abstract هنا قمنا بتعريف كلاس نوعه

	  public abstract void print();       // print() إسمها abstract هنا قمنا بتعريف دالة نوعها

	  } 
	

ركز فقط على طريقة إنشاء الكائن و تخزينه في كائن من نفس النوع.

ملاحظة: الـ Anonymous Class الذي قمنا بتعريفه يبدأ من الكلمة new إلى آخر سطر.

                    Anonymous obj = new Anonymous() {

	  @Override
	  public void print() {
	  System.out.println("Anonymous Inner Class is called");
	  }

	  }; 
	

كلما أردنا إستخدام الدالة print() نكتب التالي.

                    obj.print();
	


هنا قمنا بإنشاء Anonymous Class و قمنا باستدعاء الدالة التي فعلنا لها Override فيه مباشرةً عند تعريفه.
الأسلوب التالي يستخدم في حال كنت تريد إستدعاء الدالة التي فعلت لها Override مرة واحدة فقط.

المثال الثاني

Anonymous.java
                    public abstract class Anonymous {       // Anonymous إسمه Abstract هنا قمنا بتعريف كلاس نوعه

	  public abstract void print();       // print() إسمها abstract هنا قمنا بتعريف دالة نوعها

	  } 
	

ركز فقط على طريقة إنشاء الكائن و إستدعاء الدالة منه مباشرةً بعد أن يتم إنشاؤه.

ملاحظة: الـ Anonymous Class الذي قمنا بتعريفه يبدأ من الكلمة new إلى الرمز } الموجود في آخر سطر.

                    new Anonymous() {

	  @Override
	  public void print() {
	  System.out.println("Anonymous Inner Class is called");
	  }

	  }.print();         // Anonymous التي قنما بتعريفها بداخل الكلاس print() هنا قمنا باستدعاء الدالة 
	

هنا لا يمكن إستخدام الدالة print() من جديد لأنه لا يمكن الوصول للكائن الذي يحتويها. و بالتالي لن نستطيع إستدعاءها من جديد.

 أمثلة حول فكرة Anonymous Class و Anonymous Object في جافا

في الدروس السابقة كنا نفعل extends للكلاس المعرف كـ abstract حتى نقوم باستخدامه.
أسلوب الـ Anonymous Class يتيح لك إستخدام أي كلاس معرف كـ abstract بدون أن تفعل له extends.


هنا قمنا بتعريف كلاس نوعه abstract إسمه A, يحتوي على دالة نوعها abstract, إسمها print().
في الكلاس Main قمنا بتعريف Anonymous Class من الكلاس A و فعلنا فيه Override للدالة print().
و قمنا بتخزين كائن الـ Anonymous Class في كائن إسمه obj.

المثال الأول

A.java
                    public abstract class A {               // A إسمه Abstract هنا قمنا بتعريف كلاس نوعه

	  public abstract void print();       // print() إسمها abstract هنا قمنا بتعريف دالة نوعها

	  }
	

Main.java
                    public class Main {

	  public static void main(String[] args) {

	  // obj ثم قمنا بتخزنيه في الكائن A من Anonymous Inner Class هنا قمنا بخلق
	  A obj = new A() {
	  @Override                     // print() للدالة Override هنا فعلنا
	  public void print() {
	  System.out.println("obj is an Anonymous Inner Object");
	  }
	  };

	  // obj من الكائن print() هنا قمنا باستدعاء الدالة
	  obj.print();

	  }

	  }
	

سنحصل على النتيجة التالية عند التشغيل.

                    obj is an Anonymous Inner Object
	


في المثال التالي أعدنا نفس المثال السابق لكننا لم نقم بتخزين الكائن الذي سيرجعه كونستركتور الكلاس A في كائن من نفس النوع.
الفكرة هنا أنه يمكنك إنشاء الـ Anonymous Class و تنفيذ الدالة الموجودة فيه مباشرةً عندما يتم خلق الكائن دون الحاجة إلى تخزينه.
هذا الأسلوب مفيد في حال كنت تريد إنشاء كائن من كلاس معين و إستخدام دالة موجودة فيه مرة واحدة فقط.

المثال الثاني

A.java
                    public abstract class A {               // A إسمه Abstract هنا قمنا بتعريف كلاس نوعه

	  public abstract void print();       // print() إسمها abstract هنا قمنا بتعريف دالة نوعها

	  }
	

Main.java
                    public class Main {

	  public static void main(String[] args) {

	  // print() أيضاً للدالة Override و لم نقم بتخزينه في كائن. و فعلنا A هنا قمنا بخلق كائن من الكلاس
	  new A() {
	  @Override
	  public void print() {
	  System.out.println("obj is an Anonymous Inner Object");
	  }
	  }.print(); // مباشرةً بعد أن تم إنشاء الكائن print() هنا قمنا باستدعاء الدالة

	  }

	  }
	

سنحصل على النتيجة التالية عند التشغيل.

                    obj is an Anonymous Inner Object