Java 扩展枚举

Java 扩展枚举,java,Java,我有很多枚举,每个枚举都有相同的字段和方法 public enum AddressSubType { DOM("dom"), INTL("intl"), POSTAL("postal"); private final String keyword; private AddressSubType(String keyword) { this.keyword = keyword; } public String getKeyword() {

我有很多枚举,每个枚举都有相同的字段和方法

public enum AddressSubType {
    DOM("dom"), INTL("intl"), POSTAL("postal");

    private final String keyword;
    private AddressSubType(String keyword) {
        this.keyword = keyword;
    }
    public String getKeyword() {
        return keyword;
    }
    @Override
    public String toString() {
        return keyword;
    }
}

public enum EmailSubType {
    INTERNET("internet"), X400("x.400");

    private final String keyword;
    private EmailSubType(String keyword) {
        this.keyword = keyword;
    }
    public String getKeyword() {
        return keyword;
    }
    @Override
    public String toString() {
        return keyword;
    }
}

这些枚举是否有办法共享字段和方法(如父类)?我知道。谢谢。

您可以声明一个他们都可以实现的
接口。这将允许您将任一枚举类型作为参数传递给只关心该面上特定方法的方法。但是,这只允许“共享”方法签名,而不允许“共享”字段或方法实现

如果您的枚举与给定示例中的枚举一样微不足道,那么您没有任何明显的代码重复,因此这可能不是问题。如果发现方法具有更复杂、重复的代码,则应考虑将责任委托给单独的类。


如果您真的想为继承模式建模(例如,
EmailAddress
“是一个“
地址
),那么您需要远离
enum
。您可以只使用一些静态字段来模拟枚举模式,但每个字段都是特定类的实例。

我可能会将它们组合到一个枚举对象中,其中一些使用设置为true的“Postal”标志进行初始化,而另一些使用设置为true的“email”标志进行初始化,因为这两个对象实际上只是不同的“类型”地址

然后,如果希望单独访问迭代器,您可以让它返回迭代器,或者您可以对整个过程进行迭代

您还可能会发现代码的其他部分变得简单,例如,只是拥有一个“地址”集合,并在运行时检查给定的地址是电子邮件还是邮政地址


但这取决于它们的相似程度。

您可以创建一个价值类

public class Value {

  private final String keyword;

  private Value(String keyword) {
    this.keyword = keyword;
  }
  public String getKeyword() {
    return keyword;
  }
  @Override
  public String toString() {
    return keyword;
  }
}
然后可以创建具有公共静态最终值的类,如下所示:

public class AddressSubType extend Value {

  public static final AddressSubType DOM = new AddressSubType("DOM");
  public static final AddressSubType INTL = new AddressSubType("intl");
  ...

  private AddressSubType(String keyword) {
     super(keyword);
  }
}

我会是那个说出来的人。这是个糟糕的主意

需要表示一组固定常量时,应随时使用枚举类型。这包括自然枚举类型,例如太阳系中的行星,以及在编译时知道所有可能值的数据集,例如菜单上的选项、命令行标志等等

除了里面的硬编码值之外,枚举不关心其他任何事情。通常,当一个人决定以面向对象的方式对事物进行分组时,他们会确保所有对象都是相关的。由于这些文件是一个枚举,所以它们之间的关联不超过作为对象子类型的两个类。如果您希望在域中的所有枚举之间共享功能,那么您将希望查看一些静态函数,或者经常提到的实用程序类(这在一天结束时有它自己的一系列问题)。本质上,该类将具有一系列封装所有共享逻辑的函数,签名通常如下所示:

function foo(Enum enumeration)

在这种情况下,您可以做的不多,即使在更复杂的示例中,放置公共代码的最佳位置可能是所有枚举都可以使用的实用程序类,或者是通过组合包含在枚举中的单独类(每个枚举都有该类的实例,可能称为关键字)

如果
toString
方法的代码很复杂,您不想在每个枚举中重述它,也不想将其移动到包含的对象中,那么Java8有一种可以使用的机制。在这个例子中,这是矫枉过正。您可以定义一个枚举都将使用的接口。状态(关键字)必须仍然存在于您的枚举中,因为接口不能有状态,但从Java 8开始,您可以提供方法的默认实现:

public interface Common {
    String getKeyword();

    String toString() default {
        return getKeyword();
    }

    String toDescriptiveString() default {
        char firstLetter = getKeyword().charAt(0);
        boolean vowel =
            firstLetter == 'a' || firstLetter == 'e' ||
            firstLetter == 'i' || firstLetter == 'o' ||
            firstLetter == 'u' || firstLetter == 'x';
        // the letter x is pronounced with an initial vowel sound (eks)
        return (vowel?"an ":"a ") + getKeyword() + " address";
    }
}
您的枚举将实现此接口:

public enum AddressSubType implements Common {
    DOM("dom"), INTL("intl"), POSTAL("postal");

    private final String keyword;

    private AddressSubType(String keyword) {
        this.keyword = keyword;
    }

    @Override
    public String getKeyword() {
        return keyword;
    }

    @Override
    public String toString() {
        return Common.super.toString();
    }
}

public enum EmailSubType implements Common {
    INTERNET("internet"), X400("x.400");

    private final String keyword;

    private EmailSubType(String keyword) {
        this.keyword = keyword;
    }

    @Override
    public String getKeyword() {
        return keyword;
    }

    @Override
    public String toString() {
        return Common.super.toString();
    }
}
注意
toString
方法中奇怪的新语法。接口中默认方法的规则是方法解析总是优先于类方法而不是接口。因此,即使我们在
Common
中提供了
toString
的默认实现,枚举中的一个将优先,如果枚举中没有,则
Object
中的一个将优先。因此,如果您想从一个接口使用一个默认方法来取代
对象
中的一个方法(如
toString
,或
hashCode
,或
equals
),那么您必须使用这个新的
接口.super.method()
语法显式调用它

不过,我们不必为
toDescriptiveString
方法跳过任何额外的障碍。这是
接口Common
中全新的一个,我们的enum没有提供它,因此他们得到了接口提供的默认实现。(如果他们想用自己的方法覆盖它,他们可以,就像其他继承的方法一样。)

当然,我们可以像对象的任何其他方法一样使用默认方法:

public class Test {
    public static void main(String[] args) {
        for (AddressSubType a : AddressSubType.values()) {
            System.out.println(a.toDescriptiveString());
        }
        for (EmailSubType e : EmailSubType.values()) {
            System.out.println(e.toDescriptiveString());
        }
    }
}
打印出:

a dom address an intl address a postal address an internet address an x.400 address dom地址 国际地址 邮政地址 因特网地址 x.400地址 然而,在这种情况下,如果不是因为使用了相当详细的
toDescriptiveString
方法,使用
接口公共
的枚举类不会比没有接口公共
的枚举类短一点。当涉及到向现有接口添加新功能时,接口中的默认方法将大放异彩,如果不中断Java以前版本中接口的所有实现者,这是不可能的


所有这些都是基于目前尚不完整的数据。您可以下载预发布版本,但请注意这是一项正在进行的工作。

请参阅@Miseraverable此处的一些有用信息,谢谢。@Trashgood不,我不想将它们全部合并到一个枚举中。我想把它们分开,因为我的应用程序中的某些方法应该只接受一种枚举。我不喜欢放弃枚举的想法,但我最喜欢这个答案。所有共享代码都在父类中,子类包含所有en的静态定义