ترجمة مقال بقلم بيتر فيرهاس بتاريخ أبريل 2014. من المترجم: ظهر مصطلح " الطريقة الافتراضية " للتو في Java ولست متأكدًا مما إذا كانت هناك ترجمة ثابتة له إلى اللغة الروسية. سأستخدم مصطلح "الطريقة الافتراضية"، على الرغم من أنني لا أعتقد أنها مثالية. أدعوك لمناقشة ترجمة أكثر نجاحًا.
ما هي الطريقة الافتراضية
الآن، مع إصدار Java 8، يمكنك إضافة أساليب جديدة إلى الواجهات بحيث تظل الواجهة متوافقة مع الفئات التي تنفذها. يعد هذا أمرًا مهمًا جدًا إذا كنت تقوم بتطوير مكتبة يستخدمها العديد من المبرمجين من كييف إلى نيويورك. قبل Java 8، إذا قمت بتعريف واجهة في مكتبة، فلن تتمكن من إضافة طرق إليها دون المخاطرة بتعطل بعض التطبيقات التي تقوم بتشغيل واجهتك عند تحديثها. إذن، في Java 8، لم يعد بإمكانك الخوف من هذا؟ لا لا يمكنك. قد تؤدي إضافة طريقة افتراضية إلى الواجهة إلى جعل بعض الفئات غير قابلة للاستخدام. دعونا نلقي نظرة أولاً على الأشياء الجيدة المتعلقة بالطرق الافتراضية. في Java 8، يمكن تنفيذ الطريقة مباشرة في الواجهة. (يمكن الآن أيضًا تنفيذ الطرق الثابتة في الواجهة، ولكن هذه قصة أخرى.) تسمى الطريقة التي يتم تنفيذها في الواجهة بالطريقة الافتراضية، ويُشار إليها بالكلمة الأساسية الافتراضية . إذا قام الفصل بتنفيذ واجهة، فيمكنه، ولكن ليس مطلوبًا منه، تنفيذ الأساليب المطبقة في الواجهة. يرث الفصل التنفيذ الافتراضي. ولهذا السبب ليس من الضروري تعديل الفئات عند تغيير الواجهة التي تنفذها.تعدد الميراث؟
تصبح الأمور أكثر تعقيدًا إذا قام الفصل بتنفيذ أكثر من واجهة واحدة (على سبيل المثال، اثنتين)، ويقومون بتنفيذ نفس الطريقة الافتراضية. ما هي الطريقة التي سوف يرثها الفصل؟ الجواب لا. في هذه الحالة، يجب على الفصل تنفيذ الطريقة نفسها (إما بشكل مباشر أو عن طريق وراثتها من فئة أخرى). يكون الوضع مشابهًا إذا كانت واجهة واحدة فقط تحتوي على طريقة افتراضية، وفي الأخرى تكون نفس الطريقة مجردة. يحاول Java 8 الانضباط وتجنب المواقف الغامضة. إذا تم الإعلان عن الأساليب في أكثر من واجهة واحدة، فلن يرث الفصل أي تطبيق افتراضي - سوف تحصل على خطأ في الترجمة. على الرغم من أنك قد لا تحصل على خطأ في الترجمة إذا كان فصلك قد تم تجميعه بالفعل. Java 8 ليس قويًا بدرجة كافية في هذا الصدد. هناك أسباب لذلك لا أريد الخوض في مناقشتها (على سبيل المثال: تم إصدار إصدار Java بالفعل وقد مر وقت المناقشات منذ فترة طويلة، وبشكل عام، هذا ليس المكان المناسب لهم).- لنفترض أن لديك واجهتين ويقوم الفصل بتنفيذ كليهما.
- تطبق إحدى الواجهات الطريقة الافتراضية m().
- يمكنك تجميع كافة الواجهات والفئة.
- يمكنك تغيير واجهة لا تحتوي على طريقة m() عن طريق الإعلان عنها كطريقة مجردة.
- يمكنك تجميع الواجهة المعدلة فقط.
- ابدأ الفصل.
- قم بتغيير الواجهة باستخدام طريقة m() المجردة وأضف تطبيقًا افتراضيًا.
- تجميع الواجهة المعدلة.
- تشغيل الفئة: خطأ.
رمز المثال
لتوضيح ما سبق، قمت بإنشاء دليل اختبار لفئة C.java و3 أدلة فرعية للواجهات في الملفين I1.java وI2.java. يحتوي الدليل الجذر للاختبار على الكود المصدري لفئة C.java. يحتوي الدليل الأساسي على نسخة من الواجهات المناسبة للتنفيذ والتجميع: الواجهة I1 لها طريقة افتراضية m(); لا تحتوي واجهة I2 على أي طرق حتى الآن. يحتوي الفصل على طريقةmain
حتى نتمكن من تنفيذها لاختبارها. فهو يتحقق مما إذا كان هناك أي وسيطات لسطر الأوامر، حتى نتمكن من تنفيذها بسهولة إما مع أو بدون استدعاء m()
.
~/github/test$ cat C.java
public class C implements I1, I2 {
public static void main(String[] args) {
C c = new C();
if( args.length == 0 ){
c.m();
}
}
}
~/github/test$ cat base/I1.java
public interface I1 {
default void m(){
System.out.println("hello interface 1");
}
}
~/github/test$ cat base/I2.java
public interface I2 {
}
يمكنك ترجمة وتشغيل الفصل من سطر الأوامر.
~/github/test$ javac -cp .:base C.java
~/github/test$ java -cp .:base C
hello interface 1
يحتوي الدليل المتوافق على نسخة من واجهة I2 التي تعلن أن أسلوب m() مجرد، وأيضًا، لأسباب فنية، نسخة غير معدلة من I1.java.
~/github/test$ cat compatible/I2.java
public interface I2 {
void m();
}
لا يمكن استخدام هذه المجموعة لتجميع فئة C:
~/github/test$ javac -cp .:compatible C.java
C.java:1: error: C is not abstract and does not override abstract method m() in I2
public class C implements I1, I2 {
^
1 error
رسالة الخطأ دقيقة للغاية. ومع ذلك، لدينا C.class من تجميع سابق، وإذا قمنا بتجميع الواجهات في الدليل المتوافق، سيكون لدينا واجهتين لا يزال من الممكن استخدامهما لتشغيل الفصل:
~/github/test$ javac compatible/I*.java
~/github/test$ java -cp .:compatible C
hello interface 1
يحتوي الدليل الثالث - wrong
- على الإصدار I2، والذي يعلن أيضًا عن الطريقة m()
:
~/github/test$ cat wrong/I2.java
public interface I2 {
default void m(){
System.out.println("hello interface 2");
}
}
لا داعي للقلق بشأن التجميع. على الرغم من الإعلان عن الطريقة مرتين، إلا أنه لا يزال من الممكن استخدام الفئة وتشغيلها حتى يتم استدعاء الطريقة m(). هذا هو ما نحتاج إلى وسيطة سطر الأوامر من أجله:
~/github/test$ javac wrong/*.java
~/github/test$ java -cp .:wrong C
Exception in thread "main" java.lang.IncompatibleClassChangeError: Conflicting default methods: I1.m I2.m
at C.m(C.java)
at C.main(C.java:5)
~/github/test$ java -cp .:wrong C x
~/github/test$
GO TO FULL VERSION