【问题标题】:Java: Abstract enum with concrete implementationsJava:具有具体实现的抽象枚举
【发布时间】:2019-11-05 09:13:25
【问题描述】:

有没有办法根据枚举名称创建具有不同值的枚举抽象?

这是一个类,我会发现抽象枚举之类的东西很有用:

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
}

}

枚举的一个具体实现如下所示:

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;}

另一个实现将具有相同的枚举名称,但值不同:

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();

有没有办法以一种简洁的方式实现类似的目标?

【问题讨论】:

  • 你可以用一个接口代替AbstractEnum,不是吗?枚举可以实现一个接口。
  • 可能不是你要找的,但枚举可以有由每个常量实现的抽象方法:stackoverflow.com/questions/7413872/…
  • 这个question 可能会有所帮助。其中一个答案建议检查 Effective Java 书,我也建议。
  • @ernest_k 是的,但是我可以通过界面访问状态名称吗?我需要类似 InterfaceEnum.STATUS_ONE 的东西来传递给主类 foo()。这应该是什么样子?
  • @LakyJ 不。如果您的方法也更改为接受接口类型(它们有不同的名称,所以应该不是问题)

标签: java inheritance enums abstraction


【解决方案1】:

确实所有枚举都是对象枚举的后代,这已经构成了一个泛型类型,您可以使用它来实现独立于实际枚举实现的方法签名。

然后,您的代码会显示,对于每个“案例”,您确切地知道期望哪个枚举,因此您可以在调用专用方法时简单地class cast输入对象。

我拿了你的代码并稍作修改以证明我的建议。

两个枚举实现

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 方法签名在调用每个 case 方法时暴露了 Enum 类型的参数和 class cast

最后是一个 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));
    }
}

希望这会有所帮助!

干杯

【讨论】:

    【解决方案2】:

    这是一个使用界面的解决方案。因为枚举可以实现接口,所以您可以摆脱重载的方法(除非它们的实现不同)。

    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
    

    【讨论】:

    • 如果我把问题表述得更清楚,这似乎是一个正确的答案……我会在一分钟内更新问题。但是这一切的想法是调用主函数不应该绑定到枚举的任何具体实现。事实上,在主类中会用到多个枚举实现。一分钟后检查更新的问题。
    • @LakyJ 我想这就是我对 “实现实际上是不同的” 的意思。然后,您可以使用示例中的 if 块。这个答案的要点是,您可以使用一个接口来跨这些枚举进行抽象,并且不需要特定于枚举的方法(除非方法的实现方式不同,尽管没有说明,但似乎是这种情况)。
    【解决方案3】:

    您正在混淆定义常量和相关数据的枚举。你想定义一个“抽象枚举”的原因是,只有一组常量,所以实际的结论是应该只有一个enum

    虽然enum 类型允许直接保存关联数据,但您应该将此功能仅用于规范关联。当像您的情况一样,有不同的关联数据集时,您不应该将它们存储在 enum 类型中,因为它只允许一对一的关系。此外,不要创建另一个定义相同常量的冗余enum 类型,而是将数据存储在EnumMap 中,该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);
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-08-21
      • 2011-12-23
      • 1970-01-01
      • 1970-01-01
      • 2021-12-22
      相关资源
      最近更新 更多