java - لماذا لا توجد طرق ثابتة في الواجهات ، ولكن الحقول الثابتة والطبقات الداخلية موافق؟




interface jls (10)

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

كانت هناك بعض الأسئلة المطروحة هنا حول لماذا لا يمكنك تحديد طرق ثابتة داخل الواجهات ، لكن لا أحد منهم يعالج عدم تناسق أساسي: لماذا يمكنك تحديد حقول ثابتة وأنواع داخلية ثابتة داخل واجهة ، ولكن ليس أساليب ثابتة؟

قد لا تكون أنواع داخلية ثابتة مقارنة عادلة ، لأن هذا هو مجرد السكر النحوي الذي يولد طبقة جديدة ، ولكن لماذا الحقول ولكن لا طرق؟

إن الوسيطة ضد الأساليب الثابتة في الواجهات هي أنها تكسر إستراتيجية تحليل الجدول الظاهري المستخدمة من قبل JVM ، لكن ألا ينطبق ذلك على الحقول الثابتة بالتساوي ، أي يمكن للملجم أن يدمجها فقط؟

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


أنا غالبا ما أتساءل لماذا أساليب ثابتة على الإطلاق؟ لديهم الاستخدامات الخاصة بهم ، ولكن أساليب مستوى الحزمة / مساحة الاسم ربما تغطي 80 من ما تستخدم أساليب ثابتة ل.


الغرض من الواجهات هو تحديد العقد دون توفير التنفيذ. لذلك ، لا يمكن أن يكون لديك أساليب ثابتة ، لأنه يجب أن يكون لديك تطبيق بالفعل في الواجهة حيث لا يمكنك تجاوز الأساليب الثابتة. بالنسبة للحقول ، يتم السماح فقط final fields الثابتة ، والتي هي ، في الأساس ، الثوابت (في 1.5+ يمكنك أيضا أن يكون enums في واجهات). الثوابت موجودة للمساعدة في تعريف الواجهة بدون أرقام سحرية.

راجع للشغل ، ليس هناك حاجة لتحديد صراحة المعدلات static final للحقول في الواجهات ، لأنه يتم السماح فقط الحقول النهائية الثابتة.


ترتبط الأساليب الثابتة بفئة. في Java ، لا تعتبر الواجهة من الناحية الفنية فئة ، بل هي نوع ، ولكنها ليست فئة (وبالتالي ، فإن الكلمات الرئيسية تطبق ، والواجهات لا تمد الكائن). نظرًا لأن الواجهات ليست فئات ، فلا يمكن أن يكون لها أساليب ثابتة ، نظرًا لعدم وجود فئة فعلية مرتبطة بها.

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


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

هناك حقول ثابتة (أ) لأنها كانت موجودة في JDK 1.0 ، وتم اتخاذ العديد من القرارات المراوغة في JDK 1.0 ، و (ب) كانت الحقول النهائية الثابتة في الواجهات هي أقرب شيء كان جافا إلى الثوابت في ذلك الوقت.

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

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

بالطبع ، يمكن السماح بذلك في إصدارات JLS المستقبلية دون كسر أي شيء.


طريقة Java interface interface 1.8 مرئية لأساليب الواجهة فقط ، إذا قمنا بإزالة الطريقة methodTta1 () من فئة InterfaceExample ، فلن نتمكن من استخدامها لكائن InterfaceExample. ومع ذلك ، مثل الأساليب الثابتة الأخرى ، يمكننا استخدام أساليب ثابتة للواجهة باستخدام اسم الفئة. على سبيل المثال ، سيكون عبارة صحيحة هي: exp1.methodSta1 ()؛

لذلك ، بعد النظر في المثال أدناه يمكننا أن نقول: 1) طريقة Java interface static هي جزء من الواجهة ، ولا يمكننا استخدامها في كائنات فئة التنفيذ.

2) طرق جافا ساكن إستاتيكية جيدة لتزويد طرق المرافق ، على سبيل المثال فحص فارغ ، تصنيف الفرز ، سجل الخ.

3) يساعدنا الأسلوب الثابت لواجهة Java في توفير الأمان من خلال عدم السماح بفئات التطبيق (InterfaceExample) لتجاوزها.

4) لا يمكننا تعريف طريقة ثابتة لواجهة لطرق فئة كائن ، سوف نحصل على خطأ المحول البرمجي كـ "لا يمكن لهذه الطريقة الثابتة إخفاء أسلوب المثيل من الكائن". هذا لأنه غير مسموح به في java ، حيث أن Object هو الفئة الأساسية لجميع الفئات ، ولا يمكن أن نحصل على طريقة ثابتة واحدة على مستوى الفصل وطريقة مثيل أخرى لها نفس التوقيع.

5) يمكننا استخدام أساليب ثابتة واجهة جافا لإزالة فئات المرافق مثل مجموعات ونقل كل من أساليب ثابتة إلى واجهة المقابلة ، التي من السهل العثور عليها واستخدامها.

public class InterfaceExample implements exp1 {

    @Override
    public void method() {
        System.out.println("From method()");
    }

    public static void main(String[] args) {
        new InterfaceExample().method2();
        InterfaceExample.methodSta2();      //  <---------------------------    would not compile
        // methodSta1();                        //  <---------------------------    would not compile
        exp1.methodSta1();
    }

    static void methodSta2() {          //          <-- it compile successfully but it can't be overridden in child classes
        System.out.println("========= InterfaceExample :: from methodSta2() ======");
    }
}


interface exp1 {

    void method();
    //protected void method1();         //          <--      error
    //private void method2();           //          <--      error
    //static void methodSta1();         //          <--      error it require body in java 1.8

    static void methodSta1() {          //          <-- it compile successfully but it can't be overridden in child classes
        System.out.println("========= exp1:: from methodSta1() ======");
    }

    static void methodSta2() {          //          <-- it compile successfully but it can't be overridden in child classes
        System.out.println("========= exp1:: from methodSta2() ======");
    }

    default void method2() { System.out.println("---  exp1:: from method2() ---");}
    //synchronized default void method3() { System.out.println("---");}             // <-- Illegal modifier for the interface method method3; only public, abstract, default, static 
                                                                                // and strictfp are permitted
    //final default void method3() { System.out.println("---");} //             <--      error
}

قبل Java 5 ، كان الاستخدام الشائع للحقول الثابتة:

interface HtmlConstants {
    static String OPEN = "<";
    static String SLASH_OPEN = "</";
    static String CLOSE = ">";
    static String SLASH_CLOSE = " />";
    static String HTML = "html";
    static String BODY = "body";
    ...
}

public class HtmlBuilder implements HtmlConstants { // implements ?!?
    public String buildHtml() {
       StringBuffer sb = new StringBuffer();
       sb.append(OPEN).append(HTML).append(CLOSE);
       sb.append(OPEN).append(BODY).append(CLOSE);
       ...
       sb.append(SLASH_OPEN).append(BODY).append(CLOSE);
       sb.append(SLASH_OPEN).append(HTML).append(CLOSE);
       return sb.toString();
    }
}

هذا يعني أن HtmlBuilder لن يحتاج إلى تأهيل كل ثابت ، لذلك يمكن استخدام OPEN بدلاً من HtmlConstants.OPEN

استخدام الأدوات بهذه الطريقة هو في نهاية المطاف مربكة.

الآن مع Java 5 ، لدينا بناء جملة ثابت للاستقرار لتحقيق نفس التأثير:

private final class HtmlConstants {
    ...
    private HtmlConstants() { /* empty */ }
}

import static HtmlConstants.*;
public class HtmlBuilder { // no longer uses implements
    ...
}

قد يتم الإعلان عن الحقول النهائية الثابتة فقط في واجهة (مثل الطرق التي تكون عامة حتى إذا لم تقم بتضمين الكلمة الأساسية "العامة" ، تكون الحقول الثابتة "نهائية" مع أو بدون الكلمة الأساسية).

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


لا يوجد سبب حقيقي لعدم وجود طرق ثابتة في الواجهات ما عدا: لم يرغب مصممو لغة جافا في ذلك. من الناحية الفنية سيكون من المنطقي السماح لهم بذلك. بعد كل فئة مجردة يمكن أن يكون لهم كذلك. أفترض ولكن لم اختباره ، أنه يمكنك "كود الحرف اليدوية" بايت حيث واجهة لديها طريقة ثابتة ويجب أن يعمل imho مع أي مشاكل في استدعاء الأسلوب و / أو استخدام الواجهة كما عادة.


هذا موضوع قديم ، لكن هذا سؤال مهم جدًا للجميع. منذ أن لاحظت هذا اليوم فقط لذا أحاول شرحه بطريقة أنظف:

الهدف الرئيسي من الواجهة هو توفير شيء غير قابل للتنفيذ ، لذلك إذا كانت توفر

أساليب ثابتة ليتم السماح بها

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

الحقول الثابتة مسموح بها

لأن الحقول غير قابلة للتنفيذ ، يعني أنه قابل للتنفيذ يعني أنه لا يمكنك تنفيذ أي عملية منطقية في الميدان ، يمكنك القيام بالتشغيل في الميدان. لذا فأنت لا تغير سلوك المجال ولهذا السبب مسموح لهم بذلك.

يسمح للدروس الداخلية

يُسمح بالفصول الداخلية لأنه بعد إنشاء تصنيف فئة مصنف مختلف لفئة Inner ، يُنشئ InterfaceName $ InnerClassName.class ، لذلك تقدم بشكل أساسي التطبيق في كيان مختلف معًا وليس في الواجهة. لذلك يتم توفير التنفيذ في الفصول الداخلية.

آمل أن يساعد ذلك.







jls