[英]Java Interface that forces implementation of an enum - How to approach?
我遇到一种情况,我想使用一个称为Abstraction的对象的实例,该实例将是一个类似于以下内容的Java接口:
public interface Abstraction {
public enum Actions {
}
}
这个想法是任何实现Abstraction的类都必须实现enum 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;
}
}
这样,我可以访问接口枚举中列出的所有可能的动作,并且可以进行检查以确保要处理的动作是所创建类的availableAction之一。
接口是您希望任何人提供该合同的实施的合同。 在示例代码中,您没有方法,但是有一个名为Action的枚举的定义。
通常enum是一组常量,因此我们不希望多个类提出相同常量的不同实现。
因此,您可能需要重新考虑您的方法并找出更好的方法。 希望这将有助于您朝正确的方向前进。
我建议采用以下方法。
此方法结合使用泛型和反射来帮助明确指出实现或选择适当的枚举的需要,它还为您提供了保留有关枚举类型的信息,同时隐藏了有关特定抽象实现的所有其他信息的选项。
/**
* 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;
}
}
注意,不幸的是,由于类型 Erase,我们可以提供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.lang.Class
具有一个名为getEnumConstants()
的方法,用于获取枚举常量(如果该类不是枚举,则为null
)。 有关详细信息,请参见类API文档 。
第三,至少在泛型方面,Java在循环依赖方面是允许的,因此您可以根据泛型的特殊性来定义泛型接口。 Java不会介意的。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.