
تطورت الواجهة في Java بشكل كبير على مر السنين. دعونا نلقي نظرة على التغييرات التي حدثت في عملية تطويرها.
واجهات الأصلي
كانت الواجهات في Java 1.0 بسيطة جدًا مقارنة بما هي عليه الآن. يمكن أن تحتوي على نوعين فقط من العناصر: الثوابت وأساليب الملخص العامة.
الحقول الثابتة
يمكن أن تحتوي الواجهات على حقول ، تمامًا مثل الفصول العادية ، ولكن مع بعض الاختلافات:
- يجب تهيئة الحقول.
- تعتبر الحقول ثابتة بشكل عام
- لا يلزم تحديد المعدلات العامة والثابتة والنهائية صراحة (يتم "إخفاؤها" افتراضيًا)
public interface MyInterface { int MY_CONSTANT = 9; }
على الرغم من أن هذا لم يتم تحديده بشكل صريح ، يعتبر حقل MY_CONSTANT ثابتًا نهائيًا ثابتًا عامًا. يمكنك إضافة هذه المعدلات ، ولكن هذا ليس ضروريًا.
طرق مجردة
أهم عناصر الواجهة هي طرقها. تختلف أساليب الواجهة أيضًا عن أساليب الفصل العادية:
- طرق ليس لها الجسم
- يتم توفير طريقة التنفيذ بواسطة الفئات التي تقوم بتطبيق هذه الواجهة.
- تعتبر الطرق عامة ومجردة حتى لو لم يتم تحديدها صراحة.
- لا يمكن أن تكون الطرق نهائية ، حيث إن الجمع بين المعدلات المجردة والنهائية غير مسموح به في Java
public interface MyInterface { int doSomething(); String doSomethingCompletelyDifferent(); }
التعشيش
قدم Java 1.1 مفهوم الفئات التي يمكن وضعها داخل الفئات الأخرى. هذه الفئات هي من نوعين: ثابت وغير ثابت. يمكن أن تحتوي الواجهات أيضًا على واجهات وفئات أخرى.
حتى إذا لم يتم تحديد ذلك بشكل صريح ، فإن هذه الواجهات والفئات تعتبر عامة وثابتة.
public interface MyInterface { class MyClass {
التعدادات والشروح
تم تقديم نوعين آخرين في Java 5: التعداد والشروح. ويمكن أيضا أن توضع داخل واجهات.
public interface MyInterface { enum MyEnum { FOO, BAR; } @interface MyAnnotation {
أنواع عامة
عرض جافا 5 مفهوم الأنواع العامة. باختصار: تسمح لك الأدوية العامة باستخدام نوع عام بدلاً من تحديد نوع معين. وبالتالي ، يمكنك كتابة التعليمات البرمجية التي تعمل مع عدد مختلف من الأنواع دون التضحية بالأمان ودون تقديم تطبيق منفصل لكل نوع.
في الواجهات التي تبدأ في Java 5 ، يمكنك تحديد نوع عام ثم استخدامه كنوع قيمة الإرجاع لأحد الأساليب أو كنوع وسيطة في طريقة ما.
تعمل واجهة Box سواء كنت تستخدمها لتخزين كائنات مثل String أو Integer أو List أو Shoe أو أي شيء آخر.
interface Box<T> { void insert(T item); } class ShoeBox implements Box<Shoe> { public void insert(Shoe item) {
طرق ثابتة
بدءًا من Java 8 ، يمكنك تضمين أساليب ثابتة في الواجهات. لقد غير هذا النهج الطريقة التي تعمل بها الواجهة بالنسبة لنا. إنها تعمل الآن بشكل مختلف تمامًا عن طريقة عملها قبل Java 8. في البداية ، كانت كل الطرق في الواجهات مجردة. هذا يعني أن الواجهة قدمت توقيعًا فقط ، ولكن ليس تنفيذًا. تم ترك التطبيق للفئات التي تنفذ واجهتك.
عند استخدام الأساليب الثابتة في الواجهات ، تحتاج أيضًا إلى توفير تطبيق لنص الطريقة. لاستخدام هذه الطريقة في الواجهة ، استخدم ببساطة الكلمة الأساسية الثابتة. تعتبر الطرق الثابتة عامة بشكل افتراضي.
public interface MyInterface {
ثابت طريقة الميراث
على عكس الطرق الثابتة العادية ، لا يتم توريث الطرق الثابتة في الواجهات. هذا يعني أنه إذا كنت ترغب في استدعاء مثل هذه الطريقة ، يجب عليك الاتصال بها مباشرة من الواجهة ، وليس من الطبقة التي تنفذها.
MyInterface.staticMethod();
هذا السلوك مفيد للغاية لتجنب مشاكل التوريث المتعددة. تخيل أن لديك فئة تنفذ واجهات اثنين. كل من واجهات لديه طريقة ثابتة بنفس الاسم والتوقيع. أي واحد يجب أن تستخدم أولا؟
لماذا هو مفيد
تخيل أن لديك واجهة ومجموعة كاملة من أساليب المساعد التي تعمل مع الفئات التي تنفذ هذه الواجهة.
تقليديا ، كان هناك نهج لاستخدام فئة رفيق. بالإضافة إلى الواجهة ، تم إنشاء فئة الأداة المساعدة باسم مشابه جدًا يحتوي على طرق ثابتة تنتمي إلى الواجهة.
يمكنك العثور على أمثلة لاستخدام هذه الطريقة مباشرةً في JDK: واجهة java.util.Collection وفئة الأداة المساعدة java.util.Collections المصاحبة لها.
مع الأساليب الثابتة في واجهات ، لم يعد هذا النهج ذات الصلة ، ليست هناك حاجة وغير مستحسن. الآن يمكنك الحصول على كل شيء في مكان واحد.
الأساليب الافتراضية
الطرق الافتراضية تشبه الطرق الثابتة في أنه يجب عليك أيضًا توفير نص أساسي لها. لإعلان طريقة افتراضية ، استخدم ببساطة الكلمة الأساسية الافتراضية.
public interface MyInterface { default int doSomething() { return 0; } }
بخلاف الأساليب الثابتة ، يتم افتراضيًا توريث الطرق بواسطة الفئات التي تقوم بتطبيق الواجهة. ما هو مهم ، يمكن لهذه الفئات إعادة تعريف سلوكهم إذا لزم الأمر.
رغم وجود استثناء واحد. لا يمكن أن يكون للواجهة طرق افتراضية بنفس التوقيع مثل أساليب toString ، تساوي ، و hashCode لفئة الكائن. ألق نظرة على إجابة Brian Goetz لفهم صحة مثل هذا الحل:
اسمح للطرق الافتراضية بتجاوز أساليب الكائن.لماذا هو مفيد
لا يبدو أن فكرة تطبيق الأساليب مباشرةً في الواجهة صحيحة تمامًا. فلماذا تم تقديم هذه الوظيفة لأول مرة؟
واجهات لديها مشكلة واحدة. بمجرد أن تعطي API الخاص بك لأشخاص آخرين ، سوف "يتحول" إلى الأبد (لا يمكن تغييره بدون ألم).
حسب التقاليد ، تأخذ Java التوافق مع الإصدارات السابقة على محمل الجد. توفر الطرق الافتراضية طريقة لتوسيع الواجهات الموجودة بطرق جديدة. الأهم من ذلك ، الأساليب الافتراضية توفر بالفعل تطبيق معين. هذا يعني أن الفئات التي تنفذ واجهتك لا تحتاج إلى تنفيذ أي طرق جديدة. ولكن ، إذا لزم الأمر ، يمكن تجاوز الطرق الافتراضية في أي وقت إذا لم يعد تنفيذها مناسبًا. لذلك ، باختصار ، يمكنك توفير وظائف جديدة للفئات الموجودة التي تنفذ واجهتك ، مع الحفاظ على التوافق.
النزاعات
دعونا نتخيل أن لدينا فئة تنفذ واجهات اثنين. هذه الواجهات لها طريقة افتراضية بنفس الاسم والتوقيع.
interface A { default int doSomething() { return 0; } } interface B { default int doSomething() { return 42; } } class MyClass implements A, B { }
الآن يتم نفس الأسلوب الافتراضي مع نفس التوقيع موروثة من واجهات مختلفة اثنين. كل واجهة لها تنفيذها الخاص لهذه الطريقة.
لذا ، كيف يعرف صفنا أيًا من التطبيقين المختلفين سيستخدمان؟
لن يعرف. سينتج عن الكود أعلاه خطأ في التحويل البرمجي. إذا كنت بحاجة إلى جعلها تعمل ، فأنت بحاجة إلى تجاوز الطريقة المتعارضة في الفصل الدراسي.
interface A { default int doSomething() { return 0; } } interface B { default int doSomething() { return 42; } } class MyClass implements A, B {
طرق خاصة
مع ظهور Java 8 وإدخال الأساليب الافتراضية والأساليب الثابتة ، أصبحت الواجهات الآن لديها القدرة على احتواء ليس فقط توقيعات الطريقة ، ولكن أيضًا تنفيذها. عند كتابة هذه التطبيقات ، يوصى بتقسيم الأساليب المعقدة إلى طرق أبسط. يعد هذا الرمز أسهل في إعادة الاستخدام والصيانة والفهم.
لهذا الغرض ، يمكنك استخدام أساليب خاصة ، حيث يمكن أن تحتوي على جميع تفاصيل التنفيذ التي يجب ألا تكون مرئية ويتم استخدامها من الخارج.
لسوء الحظ في Java 8 ، لا يمكن أن تحتوي الواجهة على طرق خاصة. هذا يعني أنه يمكنك استخدام:
- طويل ومعقد ويصعب فهم تقنيات الجسم.
- الأساليب المساعدة التي تعد جزءًا من الواجهة. هذا ينتهك مبدأ التغليف ويلوث API العام للطبقات واجهة والتنفيذ.
لحسن الحظ ، بدءًا من
Java 9 ، يمكنك استخدام الأساليب الخاصة في الواجهات . لديهم الميزات التالية:
- الأساليب الخاصة لديها هيئة ، فهي ليست مجردة
- يمكن أن تكون إما ثابتة أو غير ساكنة
- لا يتم توريثها بواسطة الفئات التي تقوم بتطبيق الواجهة والواجهات
- يمكنهم استدعاء أساليب واجهة أخرى
- قد تستدعي الطرق الخاصة طرقًا خاصة أخرى أو مجردة أو ساكنة أو افتراضية
- يمكن أن تستدعي الطرق الثابتة الخاصة فقط الأساليب الساكنة الثابتة والخاصة الأخرى
public interface MyInterface { private static int staticMethod() { return 42; } private int nonStaticMethod() { return 0; } }
ترتيب زمني
فيما يلي قائمة بالتسلسل الزمني للتغييرات حسب إصدار جافا:
جافا 1.1
الطبقات المتداخلة
واجهات متداخلة
جافا 5
أنواع عامة
التحويلات المغلقة
التعليقات التوضيحية المتداخلة
جافا 8
الأساليب الافتراضية
طرق ثابتة
جافا 9
طرق خاصة