سؤال

لدي عدد كبير من Enums التي تنفذ هذه الواجهة:

/**
 * Interface for an enumeration, each element of which can be uniquely identified by it's code
 */
public interface CodableEnum {

    /**
     * Get the element with a particular code
     * @param code
     * @return
     */
    public CodableEnum getByCode(String code);

    /**
     * Get the code that identifies an element of the enum
     * @return
     */
    public String getCode();
}

مثال نموذجي هو:

public enum IMType implements CodableEnum {

    MSN_MESSENGER("msn_messenger"),
    GOOGLE_TALK("google_talk"),
    SKYPE("skype"),
    YAHOO_MESSENGER("yahoo_messenger");

    private final String code;

    IMType (String code) {
        this.code = code;
    }

    public String getCode() {
        return code;
    }   

    public IMType getByCode(String code) {
        for (IMType e : IMType.values()) {
            if (e.getCode().equalsIgnoreCase(code)) {
                return e;
            }
        }
    }
}

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

public abstract class DefaultCodableEnum implements CodableEnum {

    private final String code;

    DefaultCodableEnum(String code) {
        this.code = code;
    }

    public String getCode() {
        return this.code;
    }   

    public abstract CodableEnum getByCode(String code);  
}

ولكن تبين أن هذا عديم الفائدة إلى حد ما للأسباب التالية:

  1. لا يمكن للتعداد توسيع الفصل الدراسي
  2. لا يمكن لعناصر التعداد (SKYPE، GOOGLE_TALK، وما إلى ذلك) توسيع فئة ما
  3. لا يمكنني تقديم تطبيق افتراضي لـ getByCode()، لأن DefaultCodableEnum ليس في حد ذاته Enum.لقد حاولت تغيير DefaultCodableEnum لتوسيع java.lang.Enum، لكن يبدو أن هذا غير مسموح به.

أي اقتراحات لا تعتمد على التفكير؟شكرا ، دون

هل كانت مفيدة؟

المحلول

يمكنك تحليل الكود المكرر إلى ملف CodeableEnumHelper فصل:

public class CodeableEnumHelper {
    public static CodeableEnum getByCode(String code, CodeableEnum[] values) {
        for (CodeableEnum e : values) {
            if (e.getCode().equalsIgnoreCase(code)) {
                return e;
            }
        }
        return null;
    }
}

كل CodeableEnum لا يزال يتعين على الفصل تنفيذ ملف getByCode الطريقة، ولكن التنفيذ الفعلي للطريقة كان على الأقل مركزيًا في مكان واحد.

public enum IMType implements CodeableEnum {
    ...
    public IMType getByCode(String code) {
        return (IMType)CodeableEnumHelper.getByCode(code, this.values());
    } 
}

نصائح أخرى

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

http://freddy33.blogspot.com/2007/11/abstract-enum-ricky-carlson-way.html

الشمس آر إف إي:

http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6570766

لترتيب كود ديف:

public class CodeableEnumHelper {
    public static <E extends CodeableEnum> E getByCode(
        String code, E[] values
    ) {
        for (E e : values) {
            if (e.getCode().equalsIgnoreCase(code)) {
                return e;
            }
        }
        return null;
    }
}

public enum IMType implements CodableEnum {
    ...
    public IMType getByCode(String code) {
        return CodeableEnumHelper.getByCode(code, values());
    } 
}

أو بشكل أكثر كفاءة:

public class CodeableEnumHelper {
    public static <E extends CodeableEnum> Map<String,E> mapByCode(
        E[] values
    ) {
        Map<String,E> map = new HashMap<String,E>();
        for (E e : values) {
            map.put(e.getCode().toLowerCase(Locale.ROOT), value) {
        }
        return map;
    }
}

public enum IMType implements CodableEnum {
    ...
    private static final Map<String,IMType> byCode =
        CodeableEnumHelper.mapByCode(values());
    public IMType getByCode(String code) {
        return byCode.get(code.toLowerCase(Locale.ROOT));
    } 
}

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

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

الآن، أحتفظ بملفات التكوين والمولد، وليس كل التعليمات البرمجية المكررة.في كل مكان كنت سأحصل فيه على كود مصدر التعداد، يوجد الآن ملف تكوين XML.تكتشف نصوص الإنشاء الخاصة بي الملفات التي تم إنشاؤها قديمة وتستدعي منشئ الكود لإنشاء كود التعداد.

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

هث،
جريج

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

قم بإنشاء فئة أداة مساعدة آمنة للنوع والتي ستقوم بتحميل التعدادات حسب التعليمات البرمجية:

الواجهة تتلخص في:

public interface CodeableEnum {
    String getCode();
}

فئة المنفعة هي:

import java.lang.reflect.InvocationTargetException;


public class CodeableEnumUtils {
    @SuppressWarnings("unchecked")
    public static <T extends CodeableEnum>  T getByCode(String code, Class<T> enumClass) throws IllegalArgumentException, SecurityException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {
        T[] allValues = (T[]) enumClass.getMethod("values", new Class[0]).invoke(null, new Object[0]);
        for (T value : allValues) {
            if (value.getCode().equals(code)) {
                return value;
            }
        }
        return null;
}

}

حالة اختبار توضح الاستخدام:

import junit.framework.TestCase;


public class CodeableEnumUtilsTest extends TestCase {
    public void testWorks() throws Exception {
    assertEquals(A.ONE, CodeableEnumUtils.getByCode("one", A.class));
      assertEquals(null, CodeableEnumUtils.getByCode("blah", A.class));
    }

enum A implements CodeableEnum {
    ONE("one"), TWO("two"), THREE("three");

    private String code;

    private A(String code) {
        this.code = code;
    }

    public String getCode() {
        return code;
    }   
}
}

أنت الآن تقوم فقط بتكرار طريقة getCode() وطريقة getByCode() موجودة في مكان واحد.قد يكون من الجيد تغليف جميع الاستثناءات في RuntimeException واحد أيضًا :)

وهنا لدي حل آخر:

interface EnumTypeIF {
String getValue();

EnumTypeIF fromValue(final String theValue);

EnumTypeIF[] getValues();

class FromValue {
  private FromValue() {
  }

  public static EnumTypeIF valueOf(final String theValue, EnumTypeIF theEnumClass) {

    for (EnumTypeIF c : theEnumClass.getValues()) {
      if (c.getValue().equals(theValue)) {
        return c;
      }
    }
    throw new IllegalArgumentException(theValue);
  }
}

الحيلة هي أنه يمكن استخدام الطبقة الداخلية لاحتواء "الطرق العالمية".

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

يبدو أنك تقوم بالفعل بتنفيذ معلومات نوع وقت التشغيل.توفر Java هذا كميزة لغة.

أقترح عليك البحث عن RTTI أو التفكير.

لا أعتقد أن هذا ممكن.ومع ذلك، يمكنك استخدام طريقة valueOf(String name) الخاصة بالتعداد إذا كنت ستستخدم اسم قيمة التعداد كرمز خاص بك.

ماذا عن الطريقة العامة الثابتة؟يمكنك إعادة استخدامه من داخل أساليب getByCode() الخاصة بالتعداد الخاص بك أو ببساطة استخدامه مباشرة.أنا دائمًا أستخدم معرفات الأعداد الصحيحة للتعدادات الخاصة بي، لذا فإن طريقة getById() الخاصة بي تقوم بذلك فقط:قيم الإرجاع () [المعرف].إنه أسرع وأبسط كثيرًا.

إذا كنت تريد الميراث حقًا، فلا تنس أنك تستطيع ذلك قم بتنفيذ نمط التعداد بنفسك, ، كما هو الحال في Java القديمة السيئة 1.4 يومًا.

أقرب ما وصلت إليه مما تريده هو إنشاء قالب في IntelliJ من شأنه "تنفيذ" الكود العام (باستخدام قيمة التعداد (اسم السلسلة)).ليست مثالية ولكنها تعمل بشكل جيد.

في حالتك المحددة، تبدو أساليب getCode() / getByCode(String code) مغلقة جدًا (بمعنى ملطف) لسلوك أساليب toString() / valueOf(String value) التي توفرها جميع التعدادات.لماذا لا تريد استخدامها؟

الحل الآخر هو عدم وضع أي شيء في التعداد نفسه، وتوفير خريطة ثنائية الاتجاه لرمز Enum <-> لكل تعداد.يمكنك على سبيل المثال.يستخدم ImmutableBiMap من مجموعات جوجل لهذا الغرض.

بهذه الطريقة لا يوجد رمز مكرر على الإطلاق.

مثال:

public enum MYENUM{
  VAL1,VAL2,VAL3;
}

/** Map MYENUM to its ID */
public static final ImmutableBiMap<MYENUM, Integer> MYENUM_TO_ID = 
new ImmutableBiMap.Builder<MYENUM, Integer>().
put(MYENUM.VAL1, 1).
put(MYENUM.VAL2, 2).
put(MYENUM.VAL3, 3).
build();

في رأيي، ستكون هذه هي الطريقة الأسهل، دون تفكير ودون إضافة أي غلاف إضافي إلى التعداد الخاص بك.

يمكنك إنشاء واجهة ينفذها التعداد الخاص بك:

public interface EnumWithId {

    public int getId();

}

ثم في الفصل المساعد، يمكنك فقط إنشاء طريقة مثل هذه:

public <T extends EnumWithId> T getById(Class<T> enumClass, int id) {
    T[] values = enumClass.getEnumConstants();
    if (values != null) {
        for (T enumConst : values) {
            if (enumConst.getId() == id) {
                return enumConst;
            }
        }
    }

    return null;
}

ويمكن بعد ذلك استخدام هذه الطريقة على النحو التالي:

MyUtil.getInstance().getById(MyEnum.class, myEnumId);
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top