255 lines
12 KiB
Markdown
255 lines
12 KiB
Markdown
|
---
|
|||
|
title: Interfaces
|
|||
|
localeTitle: واجهات
|
|||
|
---
|
|||
|
# واجهات
|
|||
|
|
|||
|
واجهة في جافا قليلا مثل فئة، ولكن مع فارق كبير: و `interface` يمكن أن يكون _فقط_ تواقيع طريقة والحقول والطرق الافتراضية. منذ Java 8 ، يمكنك أيضًا إنشاء [طرق افتراضية](https://docs.oracle.com/javase/tutorial/java/IandI/defaultmethods.html) . في الجزء التالي ، يمكنك مشاهدة مثال على الواجهة:
|
|||
|
|
|||
|
`public interface Vehicle {
|
|||
|
public String licensePlate = "";
|
|||
|
public float maxVel
|
|||
|
public void start();
|
|||
|
public void stop();
|
|||
|
default void blowHorn(){
|
|||
|
System.out.println("Blowing horn");
|
|||
|
}
|
|||
|
}
|
|||
|
`
|
|||
|
|
|||
|
تحتوي الواجهة أعلاه على حقلين وطريقتين وطريقة افتراضية. وحدها ، ليست ذات فائدة كبيرة ، ولكنها تستخدم عادة مع الفصول. ماذا؟ بسيطة ، عليك أن تتأكد من أن بعض الصف يقوم `implements` .
|
|||
|
|
|||
|
`public class Car implements Vehicle {
|
|||
|
public void start() {
|
|||
|
System.out.println("starting engine...");
|
|||
|
}
|
|||
|
public void stop() {
|
|||
|
System.out.println("stopping engine...");
|
|||
|
}
|
|||
|
}
|
|||
|
`
|
|||
|
|
|||
|
![:rocket:](//forum.freecodecamp.com/images/emoji/emoji_one/rocket.png?v=2 ":صاروخ:") [تشغيل الكود](https://repl.it/CItd/0)
|
|||
|
|
|||
|
الآن ، هناك **قاعدة أساسية** : يجب على الفئة تنفيذ **كافة** الطرق في الواجهة. يجب أن تحتوي الطرق _على نفس_ التوقيع _بالضبط_ (الاسم والمعلمات والاستثناءات) كما هو موضح في الواجهة. الفئة _لا_ تحتاج إلى إعلان الحقول رغم ذلك ، فقط الطرق.
|
|||
|
|
|||
|
## مثيلات واجهة
|
|||
|
|
|||
|
بمجرد إنشاء فئة Java تقوم `implements` أي واجهة ، يمكن الإشارة إلى مثيل الكائن كمثيل للواجهة. هذا المفهوم مشابه لمبدأ إنشاء الوراثة.
|
|||
|
|
|||
|
`// following our previous example
|
|||
|
|
|||
|
Vehicle tesla = new Car();
|
|||
|
|
|||
|
tesla.start(); // starting engine ...
|
|||
|
`
|
|||
|
|
|||
|
**لا يمكن أن** تحتوي واجهة على أساليب منشئ ، لذلك **لا يمكنك** إنشاء مثيل لواجهة بنفسها. يجب إنشاء مثيل لفئة معينة تقوم بتطبيق واجهة للرجوع إليها. فكر في الواجهات على أنها نموذج عقد فارغ ، أو نموذج.
|
|||
|
|
|||
|
ما الذي يمكنك القيام به مع هذه الميزة؟ تعدد الأشكال! يمكنك استخدام الواجهات فقط للإشارة إلى مثيلات الكائن!
|
|||
|
|
|||
|
`class Truck implements Vehicle {
|
|||
|
public void start() {
|
|||
|
System.out.println("starting truck engine...");
|
|||
|
}
|
|||
|
public void stop() {
|
|||
|
System.out.println("stopping truck engine...");
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
class Starter {
|
|||
|
// static method, can be called without instantiating the class
|
|||
|
public static void startEngine(Vehicle vehicle) {
|
|||
|
vehicle.start();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
Vehicle tesla = new Car();
|
|||
|
Vehicle tata = new Truck();
|
|||
|
|
|||
|
Starter.startEngine(tesla); // starting engine ...
|
|||
|
Starter.startEngine(tata); // starting truck engine ...
|
|||
|
`
|
|||
|
|
|||
|
![:rocket:](//forum.freecodecamp.com/images/emoji/emoji_one/rocket.png?v=2 ":صاروخ:") [تشغيل الكود](https://repl.it/CItm/0)
|
|||
|
|
|||
|
## ولكن ماذا عن واجهات متعددة؟
|
|||
|
|
|||
|
نعم ، يمكنك تنفيذ واجهات متعددة في فصل واحد. أثناء [تواجدك](//forum.freecodecamp.com/t/java-docs-inheritance) في [التوارث](//forum.freecodecamp.com/t/java-docs-inheritance) داخل الصفوف ، تم تقييدك في فئة واحدة فقط ، يمكنك هنا توسيع أي عدد من الواجهات. ولكن لا ننسى تنفيذ _جميع_ أساليب جميع واجهات ، وإلا سوف تفشل التجميع!
|
|||
|
|
|||
|
`public interface GPS {
|
|||
|
public void getCoordinates();
|
|||
|
}
|
|||
|
|
|||
|
public interface Radio {
|
|||
|
public void startRadio();
|
|||
|
public void stopRadio();
|
|||
|
}
|
|||
|
|
|||
|
public class Smartphone implements GPS,Radio {
|
|||
|
public void getCoordinates() {
|
|||
|
// return some coordinates
|
|||
|
}
|
|||
|
public void startRadio() {
|
|||
|
// start Radio
|
|||
|
}
|
|||
|
public void stopRadio() {
|
|||
|
// stop Radio
|
|||
|
}
|
|||
|
}
|
|||
|
`
|
|||
|
|
|||
|
![:rocket:](//forum.freecodecamp.com/images/emoji/emoji_one/rocket.png?v=2 ":صاروخ:") [تشغيل الكود](https://repl.it/CIto/0)
|
|||
|
|
|||
|
## بعض ميزات واجهات
|
|||
|
|
|||
|
* يمكنك وضع المتغيرات داخل واجهة ، على الرغم من أنه لن يكون قرارًا معقولًا لأن الفئات غير مرتبطة بالمتغير نفسه. باختصار ، تجنب وضع المتغيرات!
|
|||
|
* تكون جميع المتغيرات والطرق في الواجهة عامة ، حتى إذا تركت الكلمة الرئيسية `public` .
|
|||
|
* لا يمكن للواجهة تحديد تنفيذ طريقة معينة. ما يصل إلى فئات للقيام بذلك. على الرغم من وجود استثناء حديث (انظر أدناه).
|
|||
|
* إذا قامت فئة بتطبيق واجهات متعددة ، فهناك احتمال بعيد لتداخل توقيع الأسلوب. بما أن Java لا تسمح بطرق متعددة من نفس التوقيع بالضبط ، فقد يؤدي هذا إلى حدوث مشكلات. انظر [هذا السؤال](http://stackoverflow.com/questions/2598009/method-name-collision-in-interface-implementation-java) لمزيد من المعلومات.
|
|||
|
|
|||
|
## طرق الواجهة الافتراضية
|
|||
|
|
|||
|
قبل Java 8 ، لم يكن لدينا طريقة لتوجيه واجهة لتطبيق طريقة معينة. هذا يؤدي إلى الكثير من الارتباك وفواصل التعليمات البرمجية إذا تم تغيير تعريف واجهة فجأة.
|
|||
|
|
|||
|
لنفترض أنك كتبت مكتبة مفتوحة المصدر تحتوي على واجهة. لنفترض أن عملاءك ، أي جميع المطورين تقريبًا في جميع أنحاء العالم ، يستخدمونها بكثافة ويسعدون. الآن كان عليك ترقية المكتبة بإضافة تعريف أسلوب جديد إلى الواجهة لدعم ميزة جديدة. ولكن هذا من شأنه أن يكسر _جميع عمليات_ البناء لأن كل الطبقات التي تقوم بتنفيذ هذه الواجهة يجب أن تتغير الآن. يا لها من كارثة!
|
|||
|
|
|||
|
لحسن الحظ ، توفر لك Java 8 الآن الطرق `default` للواجهات. _يمكن أن_ تحتوي الطريقة `default` على التنفيذ الخاص بها _مباشرة_ داخل الواجهة! لذا ، إذا لم يقم تطبيق Class بتنفيذ طريقة افتراضية ، فسيأخذ المترجم التنفيذ المذكور داخل الواجهة. جميل أليس كذلك؟ لذلك في مكتبتك ، يمكنك إضافة أي عدد من الطرق الافتراضية في واجهات دون الخوف من كسر أي شيء!
|
|||
|
|
|||
|
`public interface GPS {
|
|||
|
public void getCoordinates();
|
|||
|
default public void getRoughCoordinates() {
|
|||
|
// implementation to return coordinates from rough sources
|
|||
|
// such as wifi & mobile
|
|||
|
System.out.println("Fetching rough coordinates...");
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public interface Radio {
|
|||
|
public void startRadio();
|
|||
|
public void stopRadio();
|
|||
|
}
|
|||
|
|
|||
|
public class Smartphone implements GPS,Radio {
|
|||
|
public void getCoordinates() {
|
|||
|
// return some coordinates
|
|||
|
}
|
|||
|
public void startRadio() {
|
|||
|
// start Radio
|
|||
|
}
|
|||
|
public void stopRadio() {
|
|||
|
// stop Radio
|
|||
|
}
|
|||
|
|
|||
|
// no implementation of getRoughCoordinates()
|
|||
|
}
|
|||
|
|
|||
|
Smartphone motoG = new Smartphone();
|
|||
|
motog.getRoughCoordinates(); // Fetching rough coordinates...
|
|||
|
`
|
|||
|
|
|||
|
![:rocket:](//forum.freecodecamp.com/images/emoji/emoji_one/rocket.png?v=2 ":صاروخ:") [تشغيل الكود](https://repl.it/CItp/0)
|
|||
|
|
|||
|
### ولكن ، ماذا يحدث إذا كان للواجهتين نفس توقيع الطريقة؟
|
|||
|
|
|||
|
سؤال رائع. في هذه الحالة ، إذا لم تقدم التطبيق في الصف ، فسوف يتم خلط المترجم الضعيف ويفشل ببساطة! يجب عليك توفير تنفيذ طريقة افتراضية داخل الفئة أيضًا. هناك أيضًا طريقة رائعة تستخدم ميزة `super` للاتصال بأي تطبيق تريده:
|
|||
|
|
|||
|
`public interface Radio {
|
|||
|
// public void startRadio();
|
|||
|
// public void stopRadio();
|
|||
|
|
|||
|
default public void next() {
|
|||
|
System.out.println("Next from Radio");
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public interface MusicPlayer {
|
|||
|
// public void start();
|
|||
|
// public void pause();
|
|||
|
// public void stop();
|
|||
|
|
|||
|
default public void next() {
|
|||
|
System.out.println("Next from MusicPlayer");
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public class Smartphone implements Radio, MusicPlayer {
|
|||
|
public void next() {
|
|||
|
// Suppose you want to call MusicPlayer next
|
|||
|
MusicPlayer.super.next();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
Smartphone motoG = new Smartphone();
|
|||
|
motoG.next(); // Next from MusicPlayer
|
|||
|
`
|
|||
|
|
|||
|
![:rocket:](//forum.freecodecamp.com/images/emoji/emoji_one/rocket.png?v=2 ":صاروخ:") [تشغيل الكود](https://repl.it/CIts/0)
|
|||
|
|
|||
|
## طرق ثابتة في واجهات
|
|||
|
|
|||
|
جديد أيضا إلى Java 8 هو القدرة على إضافة أساليب ثابتة إلى واجهات. تتشابه الطرق الثابتة في الواجهات تقريبًا مع الطرق الثابتة في الفصول الخرسانية. الاختلاف الكبير الوحيد هو أن الأساليب `static` غير موروثة في الفئات التي تقوم بتطبيق الواجهة. وهذا يعني أنه يتم الإشارة إلى الواجهة عند استدعاء الأسلوب الثابت وليس الفئة التي تقوم بتطبيقه.
|
|||
|
|
|||
|
`interface MusicPlayer {
|
|||
|
public static void commercial(String sponsor) {
|
|||
|
System.out.println("Now for a message brought to you by " + sponsor);
|
|||
|
}
|
|||
|
|
|||
|
public void play();
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
class Smartphone implements MusicPlayer {
|
|||
|
public void play() {
|
|||
|
System.out.println("Playing from smartphone");
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
class Main {
|
|||
|
public static void main(String[] args) {
|
|||
|
Smartphone motoG = new Smartphone();
|
|||
|
MusicPlayer.commercial("Motorola"); // Called on interface not on implementing class
|
|||
|
// motoG.commercial("Motorola"); // This would cause a compilation error
|
|||
|
}
|
|||
|
}
|
|||
|
`
|
|||
|
|
|||
|
![:rocket:](//forum.freecodecamp.com/images/emoji/emoji_one/rocket.png?v=2 ":صاروخ:") [تشغيل الكود](https://repl.it/CIts/9)
|
|||
|
|
|||
|
## وراثة واجهة
|
|||
|
|
|||
|
من الممكن أيضًا في Java لواجهة _ترث_ واجهة أخرى ، باستخدام ، تفكر في ذلك ، `extends` الكلمة الرئيسية:
|
|||
|
|
|||
|
`public interface Player {
|
|||
|
public void start();
|
|||
|
public void pause();
|
|||
|
public void stop();
|
|||
|
}
|
|||
|
|
|||
|
public interface MusicPlayer extends Player {
|
|||
|
default public void next() {
|
|||
|
System.out.println("Next from MusicPlayer");
|
|||
|
}
|
|||
|
}
|
|||
|
`
|
|||
|
|
|||
|
هذا يعني ، أن تطبيق Class `MusicPlayer` Interface (واجهة `MusicPlayer` يجب أن يقوم بتنفيذ _جميع_ أساليب `MusicPlayer` بالإضافة إلى `Player` :
|
|||
|
|
|||
|
`public class SmartPhone implements MusicPlayer {
|
|||
|
public void start() {
|
|||
|
System.out.println("start");
|
|||
|
}
|
|||
|
public void stop() {
|
|||
|
System.out.println("stop");
|
|||
|
}
|
|||
|
public void pause() {
|
|||
|
System.out.println("pause");
|
|||
|
}
|
|||
|
}
|
|||
|
`
|
|||
|
|
|||
|
![:rocket:](//forum.freecodecamp.com/images/emoji/emoji_one/rocket.png?v=2 ":صاروخ:") [تشغيل الكود](https://repl.it/CIty/0)
|
|||
|
|
|||
|
عفوًا ، هل نسيت `next()` ؟ انظر ، لأنه كان طريقة `default` ، لم يكن لدي لتوفير التنفيذ على الإطلاق. (لن يعمل لـ JDK <8)
|
|||
|
|
|||
|
لذلك ، الآن لديك فهم جيد للواجهات! اذهب لمعرفة المزيد عن "الطبقات المستخلصة" لتعرف كيف تقدم لك جافا طريقة أخرى لتعريف العقود.
|