Рекомендация
Я бы порекомендовал следующий подход.
Этот подход использует комбинацию обобщений и рефлексии, чтобы помочь явно указать на необходимость реализации или выбора подходящего перечисления, он также даетВы можете сохранить информацию о типе перечисления, скрывая при этом всю остальную информацию о конкретной реализации 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 допускает циклические зависимости, поэтому выможет определить универсальный интерфейс зависит от специализации этого универсального.Ява не будет возражать.