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




شرح java applet بالعربي (10)

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

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

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

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

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

https://code.i-harness.com


هل يمكن استخدام == على enum ؟

نعم: يحتوي التعداد على عناصر تحكم ضيقة تسمح لك باستخدام == لمقارنة المثيلات. إليك الضمان الذي توفره مواصفات اللغة (التأكيد الخاص بي):

JLS 8.9 التعدادات

نوع التعداد ليس له مثيلات أخرى غير تلك المعرفة بواسطة ثوابته التعدادية.

وهو خطأ وقت التحويل إلى محاولة إنشاء نوع التعداد بشكل صريح. تضمن طريقة final clone في Enum عدم استنساخ ثوابت enum أبداً ، وتضمن المعالجة الخاصة من قبل آلية التسلسل عدم إنشاء حالات مكررة أبدًا كنتيجة لعملية إلغاء التسلسل. يحظر إنشاء انعكاس لأنواع التعداد. وتكفل هذه الأمور الأربعة مجتمعة عدم وجود حالات لنوع enum يتجاوز تلك التي تحددها ثوابت enum .

نظرًا لوجود مثيل واحد فقط لكل ثابت enum ، يجوز استخدام عامل == بدلاً من طريقة equals عند مقارنة مرجعين للكائن إذا كان معروفًا أن إحداها على الأقل يشير إلى ثابت enum . (طريقة equals في Enum هي طريقة final تستدعي فقط super.equals في super.equals وترجع النتيجة ، وبالتالي إجراء مقارنة للهوية.)

هذا الضمان قوي بما فيه الكفاية بحيث يوصي Josh Bloch ، إذا كنت تصر على استخدام نمط singleton ، فإن أفضل طريقة لتنفيذ ذلك هي استخدام تعداد مفرد العنصر (انظر: Java الإصدار الثاني الفعال ، العنصر 3: فرض خاصية singleton مع منشئ خاص أو نوع التعداد ؛ أيضا سلامة الصفحات في سينجلتون )

ما هي الاختلافات بين == و equals ؟

للتذكير ، يجب أن يقال أن == ليس بديلاً صالحًا equals . عندما يكون ، مع ذلك (مثل enum ) ، هناك enum مهمان يجب أخذهما في الاعتبار:

== لا يلقي أبدا NullPointerException

enum Color { BLACK, WHITE };

Color nothing = null;
if (nothing == Color.BLACK);      // runs fine
if (nothing.equals(Color.BLACK)); // throws NullPointerException

== يخضع للتحقق من نوع التوافق في وقت الترجمة

enum Color { BLACK, WHITE };
enum Chiral { LEFT, RIGHT };

if (Color.BLACK.equals(Chiral.LEFT)); // compiles fine
if (Color.BLACK == Chiral.LEFT);      // DOESN'T COMPILE!!! Incompatible types!

ينبغي == أن تستخدم عند الاقتضاء؟

يذكر Bloch على وجه التحديد أن الطبقات الثابتة التي لها السيطرة المناسبة على حالاتها يمكن أن تضمن لعملائها أن == قابلة للاستخدام. enum المذكور على وجه التحديد لتمثيل.

البند 1: النظر في أساليب المصنع ثابتة بدلا من الصانعين

[...] أنها تسمح لطبقة ثابتة بأن تضمن عدم وجود حالتين متساويتين: a.equals(b) إذا وفقط إذا كانت a==b . إذا قامت إحدى الفئات بإجراء هذه الضمانات ، فيمكن لعملائها استخدام عامل التشغيل == بدلاً من طريقة equals(Object) ، مما قد يؤدي إلى تحسين الأداء. توفر أنواع التعداد هذا الضمان.

لتلخيص ، الحجج لاستخدام == على enum هي:

  • إنها تعمل.
  • انه اسرع.
  • إنها أكثر أمانًا في وقت التشغيل.
  • إنها أكثر أمانًا في وقت الترجمة.

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

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

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

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

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

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


أود أن أسلط الضوء صراحةً على هذا الاختلاف المحدد بين طريقة == مشغل و 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 ).


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

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

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

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


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


التعداد هي عبارة عن فئات تقوم بإرجاع مثيل واحد (مثل singletons) لكل ثابت تعداد معلن بواسطة public static final field (غير قابل للتغيير) بحيث يمكن استخدام عامل == للتحقق من مساواتهم بدلاً من استخدام طريقة equals()


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

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

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


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


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

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() يعمل في معظم الحالات. إن وقت التأكد من أنك لا تقارن أنواعًا مختلفة تمامًا من الكائنات التي أشار إليها الآخرون هي صحيحة ومفيدة ، إلا أنه يمكن أيضًا العثور على نوع معين من الأخطاء التي تقارن بين نوعين من أنواع التجميع المختلفة بواسطة 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 .





enums