如何为 Java 中的枚举值提供实现?

How to provide implementations for enum values in Java?

我有枚举 class,其值假设会随着时间增长,我希望添加新枚举值的用户也能在某处提供实现。 但我不确定如何强制他们提供实施,因为实施将在其他 class 中进行。 例如

public enum DayType {
    SUNDAY,
    MONDAY;
}

在 class

中引用
class X{
DateType dateType;
..
}

并用于其他一些 class

if (x.getDateType().equals(DayType.SUNDAY)) {
...
}else if(x.getDateType().equals(DayType.MONDAY)){
..
}

因此,如果有人添加 DateType,那么他也应该被迫在上面的 if-else 逻辑中添加实现。 如果可能的话,最好通过添加功能接口?

我无法在枚举 class 中强制实施,因为实施具有 spring 依赖性。

虽然我怀疑这可能会在编译时受到限制。一种可能更简洁的实现类似方法的方法是在此处使用 switch :

switch (x.getDateType()) {
    case MONDAY: // do something
        break;
    case SUNDAY: // do something
        break;
    default:
        throw new CustomException("No implementations yet for :" + x.getDateType()); 
}

枚举旨在保存编译时(静态)最终值,因此动态添加更多值是不可能的(只是把它放在那里)。至于问题的另一部分,正如@nullpointer 指出的那样,最好使用 switch 子句。除了提高代码清晰度之外,如果枚举值没有为它们声明 case 语句(即,假设您省略了 default

,编译器会发出警告

我也不确定为用户扩展设计枚举是否是个好主意。正如@Garikai 所说,它是静态的。这会导致代码异味。

如果您希望有一个简单的选项来扩展从枚举到函数的映射,我会建议 Map<DateType, Function> map = new EnumMap<DateType, Function>(DateType.class); 然后使用 getOrDefault 以确保您将获得任何输出。

真正的答案是:不要这样做。

如果你有一些 "type",并且你知道这个 "type" 会随着时间的推移看到新的化身,需要不同的行为,那么答案就是使用抽象 class和多态性。

使用 if/else 链或 switch 语句并不重要:你有这样的想法:

if (someObject.getSomething() == whatever) { then do this } else { that }

是不好的做法!

如果有的话,您应该在工厂内使用这样的 switch 语句到 return 不同的子 class 实例,然后在这些对象上调用 "common" 方法。

您当前的方法是 A) 外部化有关内部状态的知识和 B) 还以多种方式加强依赖性。

意大利面条代码就是这样开始的!最好退后一步,考虑更多的 OOP 方法来解决这个问题!

nullpointer 选项的替代方法是将该逻辑放在枚举本身中,假设这在您的设计中有意义(如果该责任可以由 day 类型枚举承担)。

public enum DayType {
    SUNDAY {
        @Override
        void processSomeAction(Object input) {
            super.processSomeAction(input);
            // process action with SUNDAY-specific logic
        }
    },

    MONDAY;

    //can be made abstract if there's no default implementation
    void processSomeAction(Object input) {
        // process action with default logic
    }
}

这将确保对 DayType 进行更改的开发人员可以清楚地了解提供特定日期实施的需求。

调用方只需要:

x.getDateType().processSomeAction(inputIfApplicable);

您可以创建一个接口,它将由您的 Enum class 实现。在这种情况下,所有枚举都必须在接口中实现方法。

public enum DateType implements DateTypeInterface {

    SUNDAY {
        @Override
        public void checkCondition() {
            System.out.println("Implement Sunday logic");
        }
    },
    MONDAY {
        @Override
        public void checkCondition() {
            System.out.println("Implement Monday logic.");
        }
    }
}


public interface DateTypeInterface {

     public void checkCondition();
}

我认为您在编译时无法选择执行您的策略。我的建议是使用单个方法(比如 action())创建一个接口(类似于 DateTypeAction),然后创建一个基于 DateType 的值生成具体实现的工厂(类似于 DateTypeActionFactory.getDateTypeAction(DateType dateType))。在初始化阶段,您可以 运行 遍历 DateType (DateType.values()) 的所有值,并检查每个值的 DateTypeActionFactory 中是否存在 DateTypeAction 的非空实现.如果您发现一个或多个值缺少实现,则会抛出一个异常,并显示明确的错误消息,告知缺少一个实现 is/are 并导致您的应用启动失败。这似乎是一个合理的模式。

顺便说一句,如果你采用这种模式,我有一个建议:我写了一个名为 MgntUtils 的开源 java 库,它提供了一个简单的框架(非常适合与 Spring 一起使用)有一个自我填充的工厂模式。 IE。您可以创建一个接口和一个工厂扩展库,提供父级 类,然后您的接口的每个实现都将自动插入到您的工厂中,并带有预定义的名称。我用了很多次,觉得非常方便。这是描述整个库的文章的 link:MgntUtils Open Source Java library with stack trace filtering, Silent String parsing, Unicode converter and Version comparison。寻找段落

Lifecycle management (Self-instantiating factories)

功能的简短说明。您可以在此处阅读该功能的完整构想:Non-intrusive access to "Orphaned" Beans in Spring framework. Both articles explain where to get library as well, but here are the direct links: Maven Central Repository and Github。该库附带了写得很好的 javadoc

此答案扩展了此处提倡使用多态性的其他一些答案。

为了避免在某处出现不断增长的 switch/conditional,我通常会尝试在界面中添加一个识别方法:

public enum Day {
    SUNDAY, MONDAY
}

public interface DayProcessor {

    Day day();

    // I don't know what Days are supposed to do exactly
    Object process(Object input); 
}

然后我创建了一个工厂组件,其中包含所有可用的 DayProcessor 实现,这些实现由 Spring:

@Component
public class DayProcessorFactory {

    @Autowired
    private List<DayProcessor> dayProcessors;

    public DayProcessor create(Day day) {
        for (DayProcessor dayProcessor: dayProcessors) {
            if (dayProcessor.day().equals(day)) {
                return dayProcessor;
            }
        }
        // DayNotFoundException extends RuntimeException
        throw new DayNotFoundException(String.format("No DayProcessor found for day %s", day));
    }
}

这是 Strategy Pattern 的一种形式。

另一种列出所有日期的方法是枚举它们(但是当创建新的一天时您必须更新列表):

private DayProcessor[] dayProcessors = new DayProcessor[] {new MondayProcessor(), new SundayProcessor()};