enums تعلم - مقارنة أعضاء التعداد في Java:==أو يساوي()؟



شرح بالعربي (13)

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

public useEnums(SomeEnum a)
{
    if(a.equals(SomeEnum.SOME_ENUM_VALUE))
    {
        ...
    }
    ...
}

ومع ذلك ، صادفت فقط بعض التعليمات البرمجية التي تستخدم عامل equals == بدلاً من .equals ():

public useEnums2(SomeEnum a)
{
    if(a == SomeEnum.SOME_ENUM_VALUE)
    {
        ...
    }
    ...
}

ما هو المشغل الذي يجب أن أستخدمه؟


Answers

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

في ملاحظة جانبية ، لا تحتاج في الواقع إلى استخدام == لكتابة رمز آمن فارغ إذا كتبت كلمة equals() كما يلي:

public useEnums(SomeEnum a)
{
    if(SomeEnum.SOME_ENUM_VALUE.equals(a))
    {
        ...
    }
    ...
}

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


أريد أن أكمل تكملة polygenelubricants:

أنا شخصيا تفضل يساوي (). لكنها بحيرة تحقق التوافق نوع. وهو ما أعتقد أنه قيد مهم.

للحصول على التحقق من التوافق مع الكتابة في وقت التحويل البرمجي ، يمكنك التصريح واستخدام وظيفة مخصصة في التعداد الخاص بك.

public boolean isEquals(enumVariable) // compare constant from left
public static boolean areEqual(enumVariable, enumVariable2) // compare two variable

مع هذا ، لديك ميزة كل من الحل: حماية NPE ، من السهل قراءة التعليمات البرمجية ونوع التحقق من التوافق في وقت التجميع.

كما أوصي بإضافة قيمة غير محددة للتعداد.


السبب enev العمل بسهولة مع == لأن كل حالة محددة هي أيضا مفردة. لذا ستعمل مقارنة الهوية باستخدام == دائمًا.

لكن استخدام == لأنه يعمل مع التعدادات يعني أن كل كودك مقترن بإحكام باستخدام هذا التعداد.

على سبيل المثال: Enums يمكن تنفيذ واجهة. لنفترض أنك تستخدم حاليًا تعدادًا يطبّق Interface1. إذا حدث ذلك في وقت لاحق ، يقوم أحدهم بتغييره أو يقدم طبقة جديدة من Impl1 كتطبيق لنفس الواجهة. ثم ، إذا بدأت في استخدام أمثلة من Impl1 ، سيكون لديك الكثير من التعليمات البرمجية لتغيير واختبار بسبب الاستخدام السابق ==.

ومن ثم ، فمن الأفضل اتباع ما يعتبر ممارسة جيدة ما لم يكن هناك أي مكسب مبرر.


باختصار ، كلاهما له إيجابيات وسلبيات.

من ناحية ، لديها مزايا لاستخدام == ، كما هو موضح في الإجابات الأخرى.

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


في حالة التعداد كلاهما الصحيح واليمين!


كلاهما صحيح من الناحية الفنية. إذا نظرت إلى شفرة المصدر الخاصة بـ .equals() ، فهي ببساطة .equals() إلى == .

أنا أستخدم == ، ومع ذلك ، لأن ذلك سيكون آمنة فارغة.


وكما قال الآخرون ، فإن كلا من == و .equals() يعمل في معظم الحالات. إن وقت التأكد من أنك لا تقارن أنواعًا مختلفة تمامًا من الكائنات التي أشار إليها الآخرون هي صحيحة ومفيدة ، إلا أنه يمكن أيضًا العثور على نوع معين من الأخطاء التي تقارن بين نوعين من أنواع التجميع المختلفة بواسطة FindBugs (وربما عمليات فحص وقت Eclipse / IntelliJ) ، لذلك لا يضيف مترجم Java العثور عليه الكثير من الأمان الإضافي.

ومع ذلك:

  1. حقيقة أن == أبدا يرمي في NPE في رأيي هو عيب == . يجب ألا تكون هناك حاجة لأن تكون أنواع enum null ، نظرًا لأنه لا يمكن إضافة أي حالة إضافية قد ترغب في التعبير عنها عبر قيمة null إلى enum كمثيل إضافي. إذا كانت null بشكل غير متوقع ، فما استقاموا لكم فاستقيموا بدلا يكون NPE من == بصمت يقيمون إلى false. لذلك أنا اختلف مع أنها أكثر أمانا في رأي وقت التشغيل . من الأفضل أن ندخل في العادة ألا نترك قيم enum تكون @Nullable .
  2. الحجة التي تقول إن == أسرع هي أيضا مزيفة. في معظم الحالات ستقوم بالاتصال .equals() على متغير نوع وقت التحويل الخاص به هو فئة التعداد ، وفي هذه الحالات ، يمكن أن يعرف المحول البرمجي أن هذا هو نفس == (لأن طريقة enum equals() enum يمكن لا يمكن تجاوزها) ويمكن تحسين استدعاء وظيفة بعيدا. لست متأكداً مما إذا كان المترجم يقوم بذلك حالياً ، ولكن إذا لم يحدث ذلك ، وتبين أنه مشكلة في الأداء في Java بشكل عام ، فعندئذ سأقوم بإصلاح المترجم بدلاً من أن يغير 100000 مبرمج جافا أسلوبهم في البرمجة ليتلائم خصائص أداء إصدار مترجم معين.
  3. enums هي كائنات. بالنسبة لجميع أنواع الكائنات الأخرى ، تكون المقارنة القياسية .equals() ، وليس == . أعتقد أنه من الخطير أن نستثني من enums لأنك قد ينتهي بك الأمر بمقارنة الكائنات بـ == بدلاً من equals() ، خاصة إذا قمت بتعداد enum في فئة غير تعداد. في حالة إعادة البناء ، فإن نقطة العمل من الأعلى خاطئة. لإقناع نفسك أن استخدام == صحيح ، تحتاج إلى التحقق مما إذا كانت القيمة في السؤال إما enum أم بدائيًا ؛ إذا كانت فئة غير enum ، فستكون خاطئة ولكن من السهل أن تفوتها لأن التعليمة البرمجية سوف تستمر في التجميع. والحالة الوحيدة التي يكون فيها استخدام .equals() خاطئًا إذا كانت القيم المعنية .equals() ؛ في هذه الحالة ، لن يتم تجميع الشفرة ، لذا من الصعب تفويتها. وبالتالي ، فإن .equals() أسهل بكثير في التعرف على أنه صحيح ، وأكثر أمانًا ضد الرواسب المستقبلية.

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

باختصار ، ما زلت أعتقد أن الحجج هي لصالح استخدام .equals() لأنواع enum .


استخدام أي شيء آخر غير == لمقارنة ثواب التعداد هو هراء. إنها تشبه مقارنة كائنات class equals - لا تفعل ذلك!

ومع ذلك ، كان هناك خطأ مقرف (BugId 6277781 ) في Sun JDK 6u10 وأقدم في وقت سابق قد يكون مثيرا للاهتمام لأسباب تاريخية. منع هذا الخطأ الاستخدام الصحيح ل == على en Enemialized enumsial ، على الرغم من أن هذا يمكن القول إلى حد ما من حالة الزاوية.


أود أن أسلط الضوء صراحةً على هذا الاختلاف المحدد بين طريقة == مشغل و equals() :

يقصد منه الأسلوب equals() للتحقق مما إذا كانت محتويات الكائن (الكائنات) التي يشير المتغير (المتغيرات) المرجعي فيها إلى (s) هي نفسها.

يقوم عامل التشغيل == بالتحقق مما إذا كان المتغير (المراجع) المشار إليه يشير إلى (ق) إلى نفس الكائن .

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

وإلا فسيكون السلوك الافتراضي كما هو منصوص عليه في فئة Object (في Java) حيث تم شرحه في http://docs.oracle.com/javase/1.5.0/docs/api/java/lang/Object.html#equals(java.lang.Object) :

يطبق أسلوب equals للفئة Object أكثر علاقة التكافؤ الممكنة التمييز على الكائنات؛ أي ، لأي قيم مرجعية غير فارغة x و y ، ترجع هذه الطريقة true إذا وفقط إذا كانت x و y تشير إلى نفس الكائن ( x == y يحتوي على القيمة true ).


هنا هو اختبار توقيت الخام لمقارنة الاثنين:

import java.util.Date;

public class EnumCompareSpeedTest {

    static enum TestEnum {ONE, TWO, THREE }

    public static void main(String [] args) {

        Date before = new Date();
        int c = 0;

        for(int y=0;y<5;++y) {
            for(int x=0;x<Integer.MAX_VALUE;++x) {
                if(TestEnum.ONE.equals(TestEnum.TWO)) {++c;}
                if(TestEnum.ONE == TestEnum.TWO){++c;}              
            }
        }

        System.out.println(new Date().getTime() - before.getTime());
    }   

}

تعليق من IFs واحد في وقت واحد. فيما يلي مقارنة اثنين من أعلاه في رمز بايت تفكيكها:

 21  getstatic EnumCompareSpeedTest$TestEnum.ONE : EnumCompareSpeedTest.TestEnum [19]
 24  getstatic EnumCompareSpeedTest$TestEnum.TWO : EnumCompareSpeedTest.TestEnum [25]
 27  invokevirtual EnumCompareSpeedTest$TestEnum.equals(java.lang.Object) : boolean [28]
 30  ifeq 36

 36  getstatic EnumCompareSpeedTest$TestEnum.ONE : EnumCompareSpeedTest.TestEnum [19]
 39  getstatic EnumCompareSpeedTest$TestEnum.TWO : EnumCompareSpeedTest.TestEnum [25]
 42  if_acmpne 48

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

ركضت هذا الاختبار عدة مرات مع كل واحد منهما في كل مرة. يكون "==" أسرع من أي وقت مضى.


أفضل استخدام == بدلاً من equals :

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

التعداد الأول :

package first.pckg

public enum Category {
    JAZZ,
    ROCK,
    POP,
    POP_ROCK
}

التعداد الثاني:

package second.pckg

public enum Category {
    JAZZ,
    ROCK,
    POP,
    POP_ROCK
}

ثم افترض أنك تستخدم equals مثل next في item.category وهو first.pckg.Category لكنك تستورد التعداد الثاني ( second.pckg.Category ) بدلاً من الأول دون إدراك:

import second.pckg.Category;
...

Category.JAZZ.equals(item.getCategory())

لذلك سوف تحصل على allways false بسبب تعداد مختلف على الرغم من أنك تتوقع حقيقة لأن item.getCategory() هو JAZZ . ويمكن أن يكون من الصعب بعض الشيء أن نرى.

لذلك ، إذا استخدمت بدلاً من ذلك عامل التشغيل == فسيظهر خطأ تجميعي:

لا يمكن تطبيق عامل التشغيل == على "second.pckg.Category"، "first.pckg.Category"

import second.pckg.Category; 
...

Category.JAZZ == item.getCategory() 

التعداد في المنتصف عبارة عن مجموعة من الأعداد الصحيحة الثابتة. "==" صحيحة وسليمة تمامًا كما لو كنت تقارن بين عددين.


بالنسبة للقيم الرقمية ، يعد هذا أكثر أمانًا حيث إنه سيعرض أي كائن مهما كان:

public static class EnumEx
{
    static public bool TryConvert<T>(int value, out T result)
    {
        result = default(T);
        bool success = Enum.IsDefined(typeof(T), value);
        if (success)
        {
            result = (T)Enum.ToObject(typeof(T), value);
        }
        return success;
    }
}




java enums