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




interface static-methods (20)

هنا هو المثال:

public interface IXMLizable<T>
{
  static T newInstanceFromXML(Element e);
  Element toXMLElement();
}

بالطبع هذا لن ينجح. لكن لم لا؟

إحدى المشكلات المحتملة هي ما يحدث عند الاتصال:

IXMLizable.newInstanceFromXML(e);

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

تحرير: أعتقد أنني أبحث عن إجابة أعمق من "لأن هذا هو طريقة جافا".

هل هناك سبب تكنولوجي معين لماذا لا يمكن الكتابة فوق الأساليب الثابتة؟ وهذا هو السبب ، لماذا قرر مصممي جافا لجعل أساليب سبيل المثال overredeable ولكن ليست ثابتة؟

تحرير: المشكلة مع تصميمي هو محاولة استخدام الواجهات لفرض اصطلاح ترميز.

وهذا هو ، هدف الواجهة ذو شقين:

  1. أريد واجهة IXMLizable للسماح لي بتحويل الفئات التي تقوم بتطبيقها على عناصر XML (باستخدام تعدد الأشكال ، يعمل بشكل جيد).

  2. إذا أراد أحدهم إنشاء مثيل جديد لفئة تقوم بتطبيق واجهة IXMLizable ، فسيعرفون دائمًا أنه سيكون هناك منشئ ثابت newInstanceFromXML (العنصر e).

هل هناك أي طريقة أخرى لضمان ذلك ، بخلاف وضع تعليق في الواجهة؟

تحرير: اعتبارا من جافا 8 ، يسمح الآن الأساليب الثابتة في الواجهات.


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

ومع ذلك ، إذا كنت تريد أن تفعل ذلك:

public class A {
  public static void methodX() {
  }
}

public class B extends A {
  public static void methodX() {
  }
}

في هذه الحالة ما لديك فئتين مع 2 أساليب ثابتة مميزة تسمى methodX ().


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

MyImplClass.myMethod()

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

myInstance.myMethod()

ثم لا ينبغي أن تكون ثابتة. If you are actually going to use first way, but just want to enforce each implementation to have such static method, then it is really a coding convention, not a contract between instance that implements an interface and calling code.

Interfaces allow you to define contract between instance of class that implement the interface and calling code. And java helps you to be sure that this contract is not violated, so you can rely on it and don't worry what class implements this contract, just "someone who signed a contract" is enough. In case of static interfaces your code

MyImplClass.myMethod()

does not rely on the fact that each interface implementation has this method, so you do not need java to help you to be sure with it.


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

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


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

في الواقع يمكنك في Java 8.

حسب docs.oracle/staticMethod Java:

الطريقة الثابتة هي الطريقة التي ترتبط بالفئة التي يتم تعريفها فيها بدلاً من أي كائن. كل مثيل للفصل يشارك أساليبه الثابتة

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

مثال على الطريقة الافتراضية:

list.sort(ordering);

بدلا من

Collections.sort(list, ordering);

مثال على طريقة ثابتة (من docs.oracle/staticMethod نفسه):

public interface TimeClient {
    // ...
    static public ZoneId getZoneId (String zoneString) {
        try {
            return ZoneId.of(zoneString);
        } catch (DateTimeException e) {
            System.err.println("Invalid time zone: " + zoneString +
                "; using default time zone instead.");
            return ZoneId.systemDefault();
        }
    }

    default public ZonedDateTime getZonedDateTime(String zoneString) {
        return ZonedDateTime.of(getLocalDateTime(), getZoneId(zoneString));
    }    
}

لا يمكن أبدًا ISomething.member الإشارة إلى الواجهة بشكل ثابت ، مثل ISomething.member . يتم دائمًا إظهار الواجهة من خلال متغير يشير إلى مثيل لفئة فرعية من الواجهة. وبالتالي ، لا يمكن لمرجع الواجهة أبدًا معرفة الفئة الفرعية التي تشير إليها بدون مثيل من الفئة الفرعية الخاصة بها.

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

وبالتالي للحصول على أقرب تقريب لطريقة ثابتة في واجهة ، هو استخدام طريقة غير ثابتة ، ثم لا الوصول إلى أي من أعضاء مثيل غير ثابت. لن يكون هناك أي فائدة ممكنة للأداء بأي طريقة أخرى ، لأنه لا توجد طريقة لربط (في وقت التحويل البرمجي) بشكل ثابت في أي ISomething.member() . ISomething.member() . الفائدة الوحيدة التي أراها من طريقة ثابتة في واجهة هي أنها لن تقوم بإدخال (أي تجاهل) ضمني "هذا" وبالتالي عدم السماح بالوصول إلى أي من أعضاء المثيل غير الثابت. هذا من شأنه أن يعلن ضمنيًا أن الوظيفة التي لا تصل إلى "هذا" ، هي دالة وليست للقراءة فقط فيما يتعلق بالفئة المحتوية عليها. ولكن إعلان "ثابت" في واجهة ISomething قد يؤدي أيضًا إلى إرباك الأشخاص الذين حاولوا الوصول إليه باستخدام ISomething.member() مما قد يتسبب في حدوث خطأ في المحول البرمجي. أفترض إذا كان خطأ المترجم توضيحيًا بما فيه الكفاية ، سيكون أفضل من محاولة تثقيف الناس حول استخدام طريقة غير ثابتة لتحقيق ما يريدون (على ما يبدو في الغالب أساليب المصنع) ، كما نفعل هنا (وتكرر لـ 3 أسئلة وأجوبة على هذا الموقع) ، لذلك فمن الواضح أن القضية ليست بديهية لكثير من الناس. كان علي أن أفكر في ذلك لفترة من الوقت للحصول على الفهم الصحيح.

إن طريقة الحصول على حقل ثابت قابل للتغيير في الواجهة هي استخدام أدوات getter و setter غير ثابتة في الواجهة ، للوصول إلى هذا الحقل الثابت في الفئة الفرعية. يمكن تعريف Sidenote ، الثابتة على ما يبدو ثابت في واجهة جافا مع static final .


لنفترض أن الطرق الثابتة مسموح بها في واجهات: * ستجبر جميع فئات التنفيذ على إعلان هذه الطريقة. * عادةً ما يتم استخدام الواجهات من خلال الكائنات ، لذلك فإن الطرق الفعالة الوحيدة في هذه الطرق هي الطرق غير الثابتة. * يمكن لأي فئة تعرف واجهة معينة استدعاء أساليبها الثابتة. ومن ثم ، يمكن تسمية "طريقة التنفيذ" "فئة ثابتة" ، ولكن فئة المتجولين لا تعرف ذلك. كيف تعرف ذلك؟ لا يوجد لديه تأخر لتخمين ذلك!

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

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

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

هذه مشكلة في meta - عندما تحتاج إلى فعل الحمار

.. بلاه بلاه

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


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

لدي العديد من تعريفات التعداد حيث قمت بتعريف حقول "id" و "displayName" مع أساليب المساعد التي تقوم بتقييم القيم لأسباب مختلفة. يتيح تنفيذ الواجهة إمكانية التأكد من أن طرق getter موجودة ولكن ليس أساليب المساعد الثابت. كونه تعدادًا ، لا توجد طريقة نظيفة لإفراغ طرق المساعدة في فئة مجردة موروثة أو شيء من هذا القبيل ، لذلك يجب تحديد الطرق في التعداد نفسه. أيضاً لأنه تعداد ، لن تتمكن من تمريره في الواقع ككائن تم إنشاؤه ومعالجته كنوع الواجهة ، ولكن القدرة على طلب وجود طرق المساعد الثابت من خلال الواجهة هي ما يعجبني يتم دعمه في Java 8.

هنا رمز يوضح وجهة نظري.

تعريف الواجهة:

public interface IGenericEnum <T extends Enum<T>> {
    String getId();
    String getDisplayName();
    //If I was using Java 8 static helper methods would go here
}

مثال على تعريف تعدادي واحد:

public enum ExecutionModeType implements IGenericEnum<ExecutionModeType> {
    STANDARD ("Standard", "Standard Mode"),
    DEBUG ("Debug", "Debug Mode");

    String id;
    String displayName;

    //Getter methods
    public String getId() {
        return id;
    }

    public String getDisplayName() {
        return displayName;
    }

    //Constructor
    private ExecutionModeType(String id, String displayName) {
        this.id = id;
        this.displayName = displayName;
    }

    //Helper methods - not enforced by Interface
    public static boolean isValidId(String id) {
        return GenericEnumUtility.isValidId(ExecutionModeType.class, id);
    }

    public static String printIdOptions(String delimiter){
        return GenericEnumUtility.printIdOptions(ExecutionModeType.class, delimiter);
    }

    public static String[] getIdArray(){
        return GenericEnumUtility.getIdArray(ExecutionModeType.class);
    }

    public static ExecutionModeType getById(String id) throws NoSuchObjectException {
        return GenericEnumUtility.getById(ExecutionModeType.class, id);
    }
}

تعريف أداة التعريف العامة:

public class GenericEnumUtility {
    public static <T extends Enum<T> & IGenericEnum<T>> boolean isValidId(Class<T> enumType, String id) {       
        for(IGenericEnum<T> enumOption : enumType.getEnumConstants()) {
            if(enumOption.getId().equals(id)) {
                return true;
            }
        }

        return false;
    }

    public static <T extends Enum<T> & IGenericEnum<T>> String printIdOptions(Class<T> enumType, String delimiter){
        String ret = "";
        delimiter = delimiter == null ? " " : delimiter;

        int i = 0;
        for(IGenericEnum<T> enumOption : enumType.getEnumConstants()) {
            if(i == 0) {
                ret = enumOption.getId();
            } else {
                ret += delimiter + enumOption.getId();
            }           
            i++;
        }

        return ret;
    }

    public static <T extends Enum<T> & IGenericEnum<T>> String[] getIdArray(Class<T> enumType){
        List<String> idValues = new ArrayList<String>();

        for(IGenericEnum<T> enumOption : enumType.getEnumConstants()) {
            idValues.add(enumOption.getId());
        }

        return idValues.toArray(new String[idValues.size()]);
    }

    @SuppressWarnings("unchecked")
    public static <T extends Enum<T> & IGenericEnum<T>> T getById(Class<T> enumType, String id) throws NoSuchObjectException {
        id = id == null ? "" : id;
        for(IGenericEnum<T> enumOption : enumType.getEnumConstants()) {
            if(id.equals(enumOption.getId())) {
                return (T)enumOption;
            }
        }

        throw new NoSuchObjectException(String.format("ERROR: \"%s\" is not a valid ID. Valid IDs are: %s.", id, printIdOptions(enumType, " , ")));
    }
}

يتيح Java 8 أساليب الواجهة الثابتة

مع Java 8 ، يمكن أن تحتوي الواجهات على أساليب ثابتة. يمكن أن يكون لها أيضًا طرق محددة ، ولكنها لا تحتوي على أمثلة.

هناك حقا سؤالان هنا:

  1. لماذا ، في الأيام القديمة السيئة ، لا يمكن واجهات تحتوي على أساليب ثابتة؟
  2. لماذا لا يمكن تجاوز الطرق الثابتة؟

طرق ثابتة في الواجهات

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

وأخيرًا ، أدخلت Java 8 أساليب الواجهة الثابتة ، بالإضافة إلى أساليب المثيل القديرة للتطبيق مع التنفيذ الافتراضي. لا يزالوا لا يستطيعون الحصول على حقول المثال رغم ذلك. هذه الميزات جزء من دعم التعبير lambda ، ويمكنك قراءة المزيد عنها في الجزء H من JSR 335.

تجاوز الأساليب الثابتة

الجواب على السؤال الثاني أكثر تعقيدًا بعض الشيء.

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

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

التظاهر بأن كل فصل يحتوي على جدول تجزئة يقوم بتعيين خرائط التواقيع (أنواع الاسم والمعلمات) إلى جزء فعلي من التعليمات البرمجية لتنفيذ الطريقة. عندما يحاول الجهاز الظاهري استدعاء أسلوب على مثيل ، فإنه يستعلم عن الكائن لفئته ويبحث عن التوقيع المطلوب في جدول الفصل. إذا تم العثور على نص الطريقة ، فسيتم استدعاؤه. وإلا ، يتم الحصول على الفئة الأصل للفئة ، وتكرار البحث هناك. NoSuchMethodError هذا حتى يتم العثور على الطريقة ، أو لا توجد المزيد من الفئات الأصل - مما يؤدي إلى NoSuchMethodError .

إذا كان لكل من الطبقة الفائقة والفئة الفرعية كلاهما مدخل في الجداول الخاصة بهما لتوقيع الطريقة نفسها ، فستتم مصادفة إصدار الطبقة الفرعية أولاً ، ولن يتم استخدام إصدار الطبقة العليا مطلقًا - وهذا هو "تجاوز".

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

منشئ "واجهات"

إليك المزيد من المواد لمعالجة التعديل الأخير على السؤال.

يبدو أنك تريد تفويض أسلوب يشبه منشئ لكل تطبيق IXMLizable . نسيان محاولة فرض هذا باستخدام واجهة لمدة دقيقة ، والتظاهر بأن لديك بعض الفئات التي تلبي هذا المطلب. كيف يمكنك أن تستفيد منها؟

class Foo implements IXMLizable<Foo> {
  public static Foo newInstanceFromXML(Element e) { ... }
}

Foo obj = Foo.newInstanceFromXML(e);

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

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


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


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

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

cmd = createCmd(Person.getCreateCmdId());
Person p = cmd.execute();

ولتحميل شخص بواسطة المعرّف الذي ستفعله

cmd = createCmd(Person.getGetCmdId());
cmd.set(ID, id);
Person p = cmd.execute();

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

يقوم EJBs بحل هذه المشكلة عن طريق وجود واجهة منزلية ؛ كل كائن يعرف كيفية العثور على الصفحة الرئيسية والمنزل يحتوي على أساليب "ثابتة". بهذه الطريقة يمكن تجاوز الطرق "الثابتة" حسب الحاجة ، ولا تفرط في الواجهة العادية (يطلق عليها "عن بعد") بطرق لا تنطبق على مثيلتك. فقط اجعل الواجهة العادية تحدد طريقة "getHome ()". قم بإرجاع نسخة من الكائن الرئيسي (والتي يمكن أن تكون مفردة ، أفترض) ويمكن للمتصل إجراء عمليات تؤثر على جميع كائنات الأشخاص.


لأنه لا يمكن تجاوز الطرق الثابتة في الفئات الفرعية ، وبالتالي لا يمكن أن تكون مجردة. وجميع الطرق في الواجهة هي ، في الواقع ، مجردة.


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

ولكن يمكنك وضع فئات تحتوي على أساليب ثابتة داخل الواجهات. يمكنك أن تجرب ذلك!

public interface Test {
    static class Inner {
        public static Object get() {
            return 0;
        }
    }
}

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

وثانيا ، أننا نستخدم أساليب ثابتة بكل الوسائل ، هناك سبب وجيه لوجود نمط واجهة يتضمن أساليب ثابتة - لا أستطيع التحدث عن أي منكم ، لكني أستخدم أساليب ثابتة على أساس منتظم.

الإجابة الصحيحة الأكثر احتمالاً هي أنه لم تكن هناك حاجة ملحوظة ، في الوقت الذي تم فيه تعريف اللغة ، من أجل الأساليب الثابتة في الواجهات البينية. نمت جافا كثيرا على مر السنين وهذا هو البند الذي يبدو أنه اكتسب بعض الاهتمام. يشير ذلك إلى أن Java 7 يشير إلى أن مستوى ارتفاعه قد يؤدي إلى تغيير اللغة. أنا ، لأحد ، سوف يكون سعيدا عندما لم يعد لدي حاجة إلى إنشاء كائن فقط حتى أتمكن من استدعاء طريقة getter غير ثابتة للوصول إلى متغير ثابت في مثيل فئة فرعية ...


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

إذا كنت ترغب في استخدام الإحصائيات ، استخدم فئة الملخص وارثها ، وإلا فقم بإزالة المادة الثابتة.

امل ان يساعد!


مع قدوم Java 8 أصبح من الممكن الآن كتابة الطرق الافتراضية والثابتة في الواجهة. docs.oracle/staticMethod

فمثلا:

public interface Arithmetic {

    public int add(int a, int b);

    public static int multiply(int a, int b) {
        return a * b;
    }
}
public class ArithmaticImplementation implements Arithmetic {

    @Override
    public int add(int a, int b) {
        return a + b;
    }

    public static void main(String[] args) {
        int result = Arithmetic.multiply(2, 3);
        System.out.println(result);
    }
}

النتيجة : 6

تلميح: لا يتطلب تنفيذ أي طريقة من فئات الاتصال استدعاء طريقة واجهة ثابتة. من المؤكد أن هذا يحدث لأن القواعد نفسها للطرق الثابتة في الطبقة الفائقة تنطبق على الطرق الثابتة على السطوح البينية.


  • "هل هناك سبب معين لا يمكن تجاوز الطرق الثابتة".

دعني أعيد صياغة هذا السؤال لك من خلال ملء التعريفات.

  • "هل هناك سبب معين لا يمكن حل الطرق في وقت التحويل البرمجي في وقت التشغيل."

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


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

SI si = clazz.getStatic(SI.class); // null if clazz doesn't implement SI
// alternatively if the class is known at compile time
SI si = Someclass.static.SI; // either compiler errror or not null

ثم يمكنك استدعاء si.method(params) . هذا من شأنه أن يكون مفيدا (لنمط تصميم المصنع على سبيل المثال) لأنه يمكنك الحصول على (أو التحقق من تنفيذ) SI تنفيذ أساليب ثابتة من فئة غير معروفة وقت التجميع! إرسال حيوي ضروري ويمكنك تجاوز الأساليب الثابتة (إذا لم يكن نهائياً) للفئة بتمديده (عند استدعاؤه من خلال واجهة ثابتة). من الواضح أن هذه الطرق لا يمكنها الوصول إلا إلى المتغيرات الثابتة لفئتها.


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


عادة ما يتم ذلك باستخدام نمط مصنع

public interface IXMLizableFactory<T extends IXMLizable> {
  public T newInstanceFromXML(Element e);
}

public interface IXMLizable {
  public Element toXMLElement();
}

افترض أنك تستطيع القيام بذلك ؛ ضع في اعتبارك هذا المثال:

interface Iface {
  public static void thisIsTheMethod();
}

class A implements Iface {

  public static void thisIsTheMethod(){
    system.out.print("I'm class A");
  }

}

class B extends Class A {

  public static void thisIsTheMethod(){
    System.out.print("I'm class B");
  } 
}

SomeClass {

  void doStuff(Iface face) {
    IFace.thisIsTheMethod();
    // now what would/could/should happen here.
  }

}




static-methods