اذهب إلى المحتوى

عمليات على الأصناف ودوالها، آلية عمل مصفوفة ArrayList ومدخل إلى الواجهات (Interfaces) في لغة جافا


أحمد النوبي

فيما سبق من دروس هذه السلسلة، تعلمنا أساسيات لغات البرمجة وجافا خاصة، هناك بعض الأمور الهامة في لغة جافا والتي يتم استخدامها بكثرة في تطبيقات الأندرويد، وسنكمل في هذا الدرس ما بدأناه من أساسيات لغة Java.

java-oop.png

Method Overriding

لشرح هذا المفهوم دعنا نوضح هذا المثال والذي يقوم بتعريف صنف جديد يدعى Shape وبداخله ثلاث توابع كما يلي:

class Shape{

	protected int width;
	protected int height;

	public void setWidth(int a){
		width = a;
	}

	public void setHeight(int b){
		height = b;
	}

	public int getArea(){
		return 0;
	}
}

وإذا قمنا بعمل كائن جديد يمكننا من خلاله استدعاء التوابع الخاصة به.

Shape sh = new Shape();

sh.setWidth(10);
sh.setHeight(5);

int area = sh.getArea();

في المثال السابق سيتم تخزين 0 في المتغير area وذلك ما يفعله التابع ()getArea، حيث يقوم دائمًا بإعادة القيمة 0 أيًا كانت قيمة الطول والعرض وذلك لأننا نعتبر هذا الصنف نوعًا عام غير محدد الشكل ولا يمكننا معرفة مساحته.

سنقوم الآن بصنع صنف جديد يرث من Shape:

class Square extends Shape{

}

كما ذكرنا سابقًا سيرث منه المتغيرات والخصائص كما سيرث منه التوابع الخاصة به فلا داعي لتعريفها بداخله مرة أخرى.

Square sq = new Square();

sq.setWidth(10);
sq.setHeight(10);

int area = sq.getArea();

ستظل في هذه الحالة قيمة المتغير area كما هي تساوي 0 حيث ورث الصنف Square التابع ()getArea كما هو دون أي تغيير.

إذا أردنا تغيير القيمة التي يعيدها هذا التابع نستخدم مفهوم Method Overriding، وهو ببساطة يعني تجاوز المحتوى السابق لهذا التابع والذي تم استخدامه داخل الأب لهذا الصنف واستخدام محتوى جديد بدلًا منه عند استدعاء التابع.

ولتطبيق هذا المفهوم نقوم بكتابة التابع والحفاظ على اسمه ونوع البيانات التي يُعيدها وتغيير المحتوى الداخلي له لتنفيذ الوظيفة الجديدة.

class Square extends Shape{

	public int getArea(){
		return width * height;
    }
}

والآن عند كتابة الكائن السابق سنجد أن المتغير area تغيرت قيمته ليقوم بتخزين حاصل ضرب الطول والعرض وحساب المساحة، ولن يتغير المحتوى الخاص بالتابع الأصلي ()getArea المتواجد داخل الصنف Shape.

Square sq = new Square();

sq.setWidth(10);
sq.setHeight(10);

int area = sq.getArea(); //area = 10

والميزة الرئيسية لهذا المفهوم أنه يجعل للصنف الذي يرث من صنف آخر القدرة على صنع خطواته الخاصة لتنفيذ أحد التوابع التي يرثها دون التغيير في التابع الأصلي للأب.

قواعد تطبيق مفهوم Method Overriding

  1. يجب كتابة التابع دون تغيير في الاسم الخاص به أو تغيير نوع البيانات التي تُمرر له أو تغيير ترتيبها الأصلي كما لا يمكن تغيير نوع البيانات التي يُعيدها.
  2. غير مسموح بتقليل القيود المتواجدة في التابع والتي يتم تحديدها عن طريق Access Modifiers. فمثلًا إذا تم تعريف التابع على أنه public فلا يمكن تقييده وجعله protected أو private لأن ذلك أقل في صلاحيات الوصول لهذا التابع، أما إذا كان التابع الأصلي protected وتم تغييرها إلى public فهذا مسموح به لأنه أعطى صلاحية وصول أكبر للتابع.
  3. لا يمكن تجاوز التابع المُعرف على أنه final. ولاحظ أن final عند تعريف المتغيرات تعني ثابت لا يمكن تغيير قيمته، وعند تعريف التوابع تعني تابع لا يمكن تجاوزه وتغيير محتواه.
  4. إذا أردت تنفيذ محتوى التابع الأصلي لتابع تم تجاوزه يمكنك ذلك عن طريق استخدام super، وبتطبيق ذلك على المثال السابق:
class Square extends Shape{

	public int getArea(){
		int a = super.getArea();
		if ( a == 0 ){
			return width * height;
		}
		else {
			return a;
		}
	}
}

الحواشي Annotations

هي طريقة لتقديم معلومات معينة عن الشيفرة المكتوبة والتي تشير إليها هذه الملاحظة ولا يتم اعتبارها أنها جزء من الشيفرة ولا تؤثر بشكل مباشرة فيها.

هناك استخدامات مختلفة للحواشي annotations منها إعطاء أوامر للمترجم الخاص بالبرنامج، ولهذا الاستخدام يوجد عدة حواشي مبنية داخل جافا وهي:

Override@

ويتم استخدامها عند تجاوز تابع وذلك لجعل المترجم يقوم بالتأكد أننا نقوم حقًا بتجاوز تابع متواجد داخل الأب ونكتبه بشكل صحيح غير مخالف للقواعد.

class Square extends Shape{

	@Override
	public int getArea(){
		return width * height;
	}
}

Deprecated@

ويتم استخدامها لإشارة إلى أن هذا التابع أو الصنف تم إزالته من اللغة وأصبح قديمًا لذا لا يجوز استعماله مرة أخرى ويجب استبداله، وعند كتابة هذه الملاحظة يقوم المترجم بكتابة تحذير للمطور بذلك حتى يُذكّره.

وتعتبر الحواشي من الأشياء المهمة عند كتابة الشيفرات ومن الجيد التعود على استخدامها.

Method Overloading

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

  1. عدد المعاملات Parameters التي يتمر تمريرها.
  2. نوع المعاملات التي يتم تمريرها.
  3. ترتيب المُعاملات التي يتم تمريرها.
class Exampe{

	public int add(int x,int y){
		return x+y;
	}

	public int add(int x,int y,int z){
		return x+y+z;
	}

	public float add(float x,float y){
		return x+y;
	}
}

في المثال السابق استخدمنا نفس التابع ()add ولكن بأشكال مختلفة وقمنا بتطبيق إحدى القواعد المطلوبة في كل تابع لتحقيق شرط هذا المفهوم.

ولاحظ أنه لا يمكننا كتابة هذا الشكل:

public int add(int y,int x){
	return x+y;
}

حيث أنه لا يوجد فرق بينه وبين الشكل الأول فلا يمكننا التمييز بتغيير اسم المتغيرات.

ArrayList

يوجد داخل لغة جافا صنف يدعى ArrayList وهو يعبر عن قائمة من البيانات تتميز بالمرونة والقدرة على القيام بوظائف عديدة، ويتم تفضيلها عادة على استخدام مصفوفة البيانات التقليدية وذلك لأن المصفوفة يتم تحديدها بعدد من البيانات لا يمكن تغييره، فلا يمكن إضافة عناصر جديدة لها كما لا يمكن إزالة عناصر منها وتقليل العدد.

وهذا ما يميز ArrayList لقدرتها على تغيير حجمها والتكييف حسب البيانات المخزنة بداخلها.

كما تتميز ArrayList عن Array أو المصفوفة التقليدية بوجود توابع مختلفة تقوم بوظائف عديدة على عكس Array الذي يملك توابع.

لتعريف كائن جديد من الصنف ArrayList:

ArrayList<String> strObject = new ArrayList<String>();

في المثال السابق قمنا بتعريف كائن من الصنف ArrayList يدعى strObject ويستطيع تخزين بداخله بيانات من النوع String.

الآن بعد أن قمنا بتعريف الكائن هناك عدة توابع يمكننا استخدامها مثل:

(add(o

وهو يقوم بإضافة العنصر (o) إلى القائمة.

strObject.add(“Ahmed”); // [“Ahmed”]
strObject(“Mohamed”); // [“Ahmed”,”Mohamed”]
strObject(“Mariam”); // [“Ahmed”,”Mohamed”,”Mariam”]

في المثال السابق نضيف عناصر من النوع String إلى القائمة باستدعاء التابع ()add وتمرير له العنصر الذي نريد إضافته، وفي كل مرة يتم استدعاء التابع يتم تغيير حجم القائمة بشكل مرن.

(add(I ,o

يختلف عن التابع السابق بأنه يقوم بتحديد المكان (I) الذي يرغب بتخزين العنصر فيه، ففي التابع السابق يتم إضافة العناصر في ذيل القائمة.

strObject.add(2,“Sara”); // [“Ahmed”,”Mohamed”,”Sara”,”Mariam”]

(set(I,O

تقوم بتبديل العنصر المتواجد في المكان (I) بالعنصر (O).

strObject.set(1,“Tarek”); // [“Ahmed”,”Tarek”,”Sara”,”Mariam”]

في المثال السابق سيتم استبدال العنصر "Mohamed" بالعنصر "Tarek".

لاحظ أنه يبدأ الترقيم الخاص بالعناصر من صفر.

(get(I

يُعيد هذا التابع العنصر المخزن في المكان (I).

String name = strObject.get(0); //nama = “Ahmed”

في المثال السابق نحصل على العنصر "Ahmed" والمتواجد في المكان 0 (رأس القائمة).

()size

لمعرفة عدد العناصر المخزنة داخل القائمة.

int numberOfElements = strObject.size(); // numberOfElements = 4

(remove(O

لإزالة عنصر محدد من القائمة.

strObject.remove(“Mariam”); // [“Ahmed”,”Tarek”,”Sara”]

(remove(I

لإزالة عنصر المتواجد في المكان (I).

strObject.remove(1); // [“Ahmed”,”Sara”]

()clear

لإزالة كافة عناصر القائمة.

strObject.clear(); // []

وكما قمنا باستخدام ArrayList مع النصوص يمكننا استخدامها مع أي صنف أخر فمثلا يمكنا عمل قائمة من المربعات (صنف Square)، وهو الصنف الذي قمنا بصناعته في أول الدرس.

ArrayList<Square> obj = new ArrayList<Square>();

ولإضافة مربع جديد للقائمة.

Obj.add(new Square());

ولتغيير الطول والعرض الخاصين بهذا المربع.

Obj.get(0).setWidth(15);
Obj.get(0).setHeight(15);

وهكذا يمكننا التعامل مع عناصر القائمة بنفس الطريقة، فكل عنصر داخل القائمة هو كائن من Square.

لاحظ أنه هناك توابع خاصة بالصنف ArrayList وأخرى خاصة بالصنف Square.

Interface

تتشابه الواجهات (Interface) في بينتها مع الأصناف (Class)، فيمكننا بداخله تعريف توابع ومتغيرات لكنها ذات طبيعة خاصة.

فجميع التوابع داخل الواجهة تتكون من تعريف فقط ولا يوجد لها محتوى، وعلى الصنف الذي يُنفذ الواجهة أن يكتب المحتوى الخاص بالتابع.

public interface MyInterface {
	public int method1();
	public void method2();
}

ولا يمكن إنشاء كائنات من الواجهة (Interface).

ولتنفيذ الواجهة نستخدم implements، ويمكن للأصناف (Classes) فقط أن تُنفذ الواجهات (Interfaces).

وبداخل الصنف يجب كتابة المحتوى الخاص بالتوابع التي تم تعريفها داخل الواجهة، كما يمكننا أن نكتب التوابع الخاصة بالصنف كما سبق.

public class X implements MyInterface{

	public int method1(){
		return 0;
	}

	public void method2(){
	}
}

بعد ذلك يمكننا إنشاء كائنات من الصنف X واستدعاء التوابع كما فعلنا سابقًا.

X obj = new X();
Obj.method2();

واستخدام الواجهة هو الطريقة المثالية لتزويد المطور بالتوابع اللازم كتابتها لأداء مهمة ما، فهي تضمن أن الصنف الذي يُنفذ الواجهة قد قام بكتابة كافة التوابع الخاصة به.


تفاعل الأعضاء

أفضل التعليقات



انضم إلى النقاش

يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.

زائر
أضف تعليق

×   لقد أضفت محتوى بخط أو تنسيق مختلف.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   جرى استعادة المحتوى السابق..   امسح المحرر

×   You cannot paste images directly. Upload or insert images from URL.


×
×
  • أضف...