Интерфейс Java, который заставляет реализацию перечисления - Как подойти? - PullRequest
0 голосов
/ 19 мая 2018

У меня есть ситуация, когда я хотел бы использовать экземпляр объекта с именем Abstraction, который представлял бы собой интерфейс Java, выглядящий примерно так:

public interface Abstraction {
   public enum Actions {
   }
}

Идея состоит в том, что любой класс, реализующий Abstraction, долженреализовать перечисление Actions (я понимаю, это не работает).

Класс, реализующий интерфейс, может выглядеть следующим образом:

public class AbstractionA implements Abstraction {
   public enum Actions {
    f, c, r, a
   }
}

В другом классе я хотел бы создать объект Abstraction, например:

Abstraction abs = new AbstractionA();

и затем получить доступ к значениям перечисления, применимым к созданному объекту Abstraction, например,

abs.Action.r;

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

РЕДАКТИРОВАТЬ: другим примером реализации может быть

public class AbstractionB implements Abstraction {
   public enum Actions {
    f, c, b, r, a
   }
}

Я думаю, что нашел способ продвинуться с этим:

public interface Abstraction {
   public enum Actions {
    f, c, b, r, s, a  
   }
   public Actions[] availableActions();
}

Затем реализовать с помощью:

public class HunlAbstractionA implements Abstraction{
   @Override
   public Actions[] availableActions()
   {
    Actions[] actions = new Actions[] {Actions.f, Actions.c, Actions.r, Actions.a};
    return actions;
   }
}

Таким образом, у меня есть доступ ко всем возможным действиям, перечисленным в перечислении интерфейсов.и может делать проверки, чтобы гарантировать, что действие, которое должно быть обработано, является одним из доступных действий для созданного класса.

Ответы [ 2 ]

0 голосов
/ 19 мая 2018

Рекомендация

Я бы порекомендовал следующий подход.

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

/**
 * An abstraction with an implementation-defined enum
 * @param <E> your custom enum.
 */
interface Abstraction<E extends Enum> {

    //this gives you the enum constants as a list
    Class<E> getEnumType();

}

class AbstractionA implements Abstraction<AbstractionA.EnumA> {
    enum EnumA {
        FOO,
        BAR
    }

    @Override
    public Class<EnumA> getEnumType() {
        return EnumA.class;
    }
}

class AbstractionB implements Abstraction<AbstractionB.EnumB> {
    enum EnumB {
        FOO,
        BAR
    }

    @Override
    public Class<EnumB> getEnumType() {
        return EnumB.class;
    }
}

Обратите внимание, что, к сожалению, мы можем предоставить реализацию getEnumType () по умолчанию из-за стирания типа .

Пример использования

class Main {
    public static void main(String[] args) {
        Abstraction myAbstractionA = new AbstractionA();
        Abstraction<AbstractionB.EnumB> myAbstractionB = new AbstractionB();

        Class enumAType = myAbstractionA.getEnumType();
        Class<AbstractionB.EnumB> enumBType = myAbstractionB.getEnumType();
        Object[] enumsA = enumAType.getEnumConstants();
        AbstractionB.EnumB[] enumsB = enumBType.getEnumConstants();
        System.out.printf("Enums of the same order are still non-identical: %s", enumsA[0].equals(enumsB[0]));
        System.out.println();

        Enum enumA = ((Enum)enumsA[0]);
        Enum enumB = ((Enum)enumsB[1]);
        System.out.printf("We can get enum constants in order, and get the orderinal of the enum: A=%s, B=%s", enumA.ordinal(), enumB.ordinal());
        System.out.println();

        enumA = Enum.valueOf(enumAType, "FOO");
        enumB = Enum.valueOf(enumBType, "BAR");
        System.out.printf("We can get enum constants by name and get the name out of the enum: A=%s, B=%s", enumA.name(), enumB.name());
        System.out.println();
    }
}

Альтернативы

Если вы можете использовать абстрактный интерфейс вместо интерфейса, вы можете предпочесть решение, подобное этот связанный ответ .

Редактировать : если у вас есть общий набор констант, которыми вы хотите поделиться в своих действиях, вам, вероятно, следует использовать глобальное / общее перечисление дляэти константы и определяют только сами расширения в пользовательских абстракциях.Если вы приведете их к Enum и при необходимости будете использовать .equals(), это будет работать в большинстве случаев.

Фон

Как вы уже сказали, вы знаете, что не можно размещать объекты-члены (переменные или классы) интерфейса.

Тем не менее, хорошая новость заключается в том, что Java действительно поддерживает желаемое поведение.

Существует 3 ключевых функции, которыеотноситесь к моей рекомендации:

Перечисления являются объектами

Во-первых, перечисления в java являются полноценными Object с, которые все расширяются java.lang.Enum, и все реализуют .equals().

Таким образом, вы можете хранить различные значения любого перечисляемого класса в переменной типа java.lang.Enum и сравнивать их с .equals().

И , если хотите притворитьсячто значения различных классов перечисления одинаковы, так как они имеют одно и то же имя (или являются n-й константой в соответствующем классе), вы также можете сделать это.

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

Подробнее см. в документации Enum API .

Отражение Java

Во-вторых, Java имеет обширныйподдержка отражения.Для наших целей java.lang.Class имеет метод, называемый getEnumConstants() для получения констант перечисления (или null, если класс не является перечислением).Подробнее см. Документацию API класса .

Циклические зависимости

В-третьих, по крайней мере, когда дело касается дженериков, Java допускает циклические зависимости, поэтому выможет определить универсальный интерфейс зависит от специализации этого универсального.Ява не будет возражать.

0 голосов
/ 19 мая 2018

Интерфейс - это контракт, который вы хотите, чтобы кто-либо предоставил для реализации этого контракта.В вашем примере кода у вас нет метода, но есть определение перечисления с именем Action.

Обычно enum - это набор констант, поэтому мы не ожидаем, что несколько классов придумают разные реализации одной и той же константы.

Так что вы можете переосмыслить свой подход и найти лучший способ.Надеюсь, это поможет вам двигаться в правильном направлении.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...