Java: Абстрактное перечисление с конкретными реализациями - PullRequest
1 голос
/ 05 ноября 2019

Есть ли способ создать абстракцию перечисления с различными значениями на основе имен перечисления?

Вот класс, в котором я бы нашел что-то вроде абстрактного перечисления полезным:

public class StatusUpdater {
public foo(bool one, bool two, bool three, AbstractEnum status) {
    if (one) {
        caseOne(status);
    }

    if (two) {
        caseTwo(status);
    }

    if (three) {
        caseThree(status);
    }
}

private void caseOne(CaseOneEnum status) {
    status.getValue(); //return case one value + case one implementation
}

private void caseTwo(CaseTwoEnum status) {
    status.getValue(); //return case two value + case two implementation
}

private void caseThree(CaseThreeEnum status) {
    status.getValue(); //return case three value + case three implementation
}

}

Одна из конкретных реализаций enum выглядит следующим образом:

public enum CaseOneEnum {
STATUS_ONE("one"),
STATUS_TWO("two");

private final String status;

CaseOneEnum(final String status) {
    this.status = status;
}

@Override
String getValue(){return status;}

Другая реализация будет иметь те же имена enum, но разные значения:

public enum CaseTwoEnum {
STATUS_ONE("oneTwo"),
STATUS_TWO("twoTwo");

private final String status;

CaseTwoEnum(final String status) {
    this.status = status;
}

@Override
String getValue(){return status;}

Вызов mainМетод должен выглядеть примерно так:

updater.foo(true, true, false, AbstractEnum.STATUS_ONE);

Есть ли способ, где я мог бы создать "абстрактное" перечисление, которое я мог бы передать в foo (), и после проверки случая взять конкретную реализацию этого перечисления. Имена перечислений для всех конкретных перечислений останутся прежними, но значения будут отличаться. Я бы вообразил «абстрактное» перечисление примерно так:

public enum AbstractEnum {
STATUS_ONE,
STATUS_TWO;

@Override
String getValue();

Есть ли способ достичь чего-то подобного аккуратно?

Ответы [ 3 ]

2 голосов
/ 05 ноября 2019

действительно все перечисления являются потомками объекта Enum , и это уже является универсальным типом, который вы можете использовать для реализации сигнатуры метода, независимой от фактической реализации перечисления.

Ваш код тогда показывает, чтодля каждого "случая" вы точно знаете, какое перечисление ожидать, и, таким образом, вы можете просто приведение класса объекта ввода при вызове выделенного метода.

Я взял ваш код и немного его изменилчтобы продемонстрировать то, что я предлагаю.

Две реализации перечислений

public enum CaseOneEnum {
    STATUS_ONE("one"),
    STATUS_TWO("two");

    private final String status;

    CaseOneEnum(final String status) {
        this.status = status;
    }

    String getValue() {
        return status;
    }
}

public enum CaseTwoEnum {
     STATUS_THREE("three"),
     STATUS_FOUR("four");

     private final String status;

     CaseTwoEnum(final String status) {
         this.status = status;
     }

     String getValue() {
         return status;
     }
 }

Класс StatusUpdater

public class StatusUpdater {

    public String foo(int caze, Enum<?> status) {

        if (caze == 1) {
            return caseOne((CaseOneEnum) status);
        }

        if (caze == 2) {
            return caseTwo((CaseTwoEnum) status);
        }
        return null;
    }

    private String caseOne(CaseOneEnum status) {
        return status.getValue();
    }

    private String caseTwo(CaseTwoEnum status) {
        return status.getValue();
    }
}

Самая важная вещь, на которую следует обратить внимание, это разоблачение сигнатуры метода fooпараметр типа Enum и класс приведение при вызове каждого метода case.

И, наконец, JUnit для демонстрации.

public class StatusUpdaterTest {

    @Test
    public void testStatusUpdaterCase1() {
        StatusUpdater updater = new StatusUpdater();
        assertEquals(CaseOneEnum.STATUS_ONE.getValue(), updater.foo(1, CaseOneEnum.STATUS_ONE));
}

    @Test
    public void testStatusUpdaterCase2() {
        StatusUpdater updater = new StatusUpdater();
        assertEquals(CaseOneEnum.STATUS_TWO.getValue(), updater.foo(1, CaseOneEnum.STATUS_TWO));
    }

    @Test
    public void testStatusUpdaterCase3() {
        StatusUpdater updater = new StatusUpdater();
        assertEquals(CaseTwoEnum.STATUS_THREE.getValue(), updater.foo(2, CaseTwoEnum.STATUS_THREE));
    }

    @Test
    public void testStatusUpdaterCase4() {
        StatusUpdater updater = new StatusUpdater();
        assertEquals(CaseTwoEnum.STATUS_FOUR.getValue(), updater.foo(2, CaseTwoEnum.STATUS_FOUR));
    }
}

Надеюсь, это поможет!

Ура

1 голос
/ 05 ноября 2019

Вот решение с использованием интерфейса. Поскольку перечисления могут реализовывать интерфейс, вы можете избавиться от перегруженных методов (, если их реализации не отличаются ).

interface MyEnumInterface {
    String getValue();
}

При этом ваш класс StatusUpdater становится:

public class StatusUpdater {
    public void foo(MyEnumInterface status) {
        anyCase(status);
    }

    //You can duplicate this with caseOne, caseTwo methods if
    //implementations are actually different
    private void anyCase(MyEnumInterface status) {
        status.getValue(); 
    }
}

Затем вы вызываете ваш foo метод следующим образом:

updater.foo(1, CaseOneEnum.STATUS_ONE);
updater.foo(1, CaseTwoEnum.STATUS_ONE);

И затем все, что вам остается, это заставить ваши перечисления реализовать интерфейс (у них уже есть метод):

enum CaseOneEnum implements MyEnumInterface {
    STATUS_ONE("one"), 
    STATUS_TWO("two");

    private final String status;

    CaseOneEnum(final String status) {
        this.status = status;
    }

    @Override
    public String getValue() {
        return status;
    }
}

//other enums implement the
0 голосов
/ 11 ноября 2019

Вы смешиваете перечисления, которые определяют константы и связанные данные. Причина, по которой вы хотите определить «абстрактное перечисление», заключается в том, что существует только один набор констант, поэтому фактический вывод заключается в том, что должен быть только один enum.

, в то время как типы enum позволяютЧтобы напрямую хранить связанные данные, вы должны использовать эту функцию только для канонических ассоциаций. Когда, как и в вашем случае, существуют разные наборы связанных данных, вы не должны хранить их в типе enum, так как это допускает только однозначные отношения. Кроме того, вместо создания другого избыточного типа enum, определяющего идентичные константы, сохраните данные в EnumMap, который специально предназначен для связывания данных с enum константами.

Определение простого типа enumкак

public enum YourActualEnum {
    STATUS_ONE,
    STATUS_TWO
}

и класс, имеющий различные связанные данные, в зависимости от варианта использования:

public class StatusUpdater {
    private static final EnumMap<YourActualEnum,String> CASE1, CASE2, CASE3;
    static {
        CASE1 = new EnumMap<>(YourActualEnum.class);
        CASE1.put(YourActualEnum.STATUS_ONE, "one");
        CASE1.put(YourActualEnum.STATUS_TWO, "two");
        CASE2 = new EnumMap<>(YourActualEnum.class);
        CASE2.put(YourActualEnum.STATUS_ONE, "oneTwo");
        CASE2.put(YourActualEnum.STATUS_TWO, "twoTwo");
        CASE3 = new EnumMap<>(YourActualEnum.class);
        CASE3.put(YourActualEnum.STATUS_ONE, "oneThree");
        CASE3.put(YourActualEnum.STATUS_TWO, "twoThree");
    }
    public void foo(boolean one, boolean two, boolean three, YourActualEnum status) {
        if (one) {
            caseOne(status, CASE1);
        }

        if (two) {
            caseTwo(status, CASE2);
        }

        if (three) {
            caseThree(status, CASE3);
        }
    }

    private void caseOne(YourActualEnum status, EnumMap<YourActualEnum,String> m) {
        m.get(status);
    }

    private void caseTwo(YourActualEnum status, EnumMap<YourActualEnum,String> m) {
        m.get(status);
    }

    private void caseThree(YourActualEnum status, EnumMap<YourActualEnum,String> m) {
        m.get(status);
    }
}
...