2

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

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

这些枚举有没有办法共享字段和方法(如父类)?我知道不可能扩展 enums。谢谢。

4

5 回答 5

1

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

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

如果您真的想为继承模式建模(例如EmailAddress"is a" Address),那么您需要远离enums. 您可以只使用一些静态字段来模拟枚举模式,但让它们中的每一个都是特定类的实例。

于 2012-06-25T23:59:25.333 回答
1

我可能会将它们组合成一个枚举对象,其中一些初始化时将“邮政”标志设置为真,而有些将“电子邮件”标志设置为真,因为这两者实际上只是不同的“类型”地址。

然后,如果您希望单独访问它们,或者您可以遍历整个事物,您可以让它返回迭代器。

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

但这取决于它们实际上有多相似。

于 2012-06-26T00:01:14.237 回答
1

您可以创建一个 Value 类

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);
  }
}
于 2012-06-26T00:04:45.740 回答
1

我会是那个说它的人。这是一个可怕的想法。

您应该在需要表示一组固定常量的任何时候使用枚举类型。这包括自然枚举类型,例如我们太阳系中的行星和您在编译时知道所有可能值的数据集——例如,菜单上的选择、命令行标志等。 来源

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

function foo(Enum enumeration)
于 2012-06-26T00:24:37.973 回答
1

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

如果该toString方法的代码很复杂,并且您不想在每个枚举中重新声明它,或者将其移动到包含的对象中,那么 Java 8 有一个您可以使用的机制。在这个例子中它是多余的。您可以定义一个您的枚举都将使用的接口。状态(关键字)必须仍然存在于您的枚举中,因为接口不能有状态,但从 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。接口中默认方法的规则是方法解析总是优先于类方法而不是接口。因此,即使我们提供了toStringin的默认实现Common,枚举中的那个将优先,Object如果枚举中没有一个,则 in 将优先。因此,如果您想使用接口中的默认方法取代其中一种方法Object(例如toString,或hashCode,或equals),那么您必须使用这种新interface.super.method()语法显式调用它。

不过,我们不必为该toDescriptiveString方法跳过任何额外的环节。那是一个全新的 in interface Common,它不是由我们的枚举提供的,因此它们获得了接口提供的默认实现。(如果他们想用自己的方法覆盖它,他们可以,就像任何其他继承的方法一样。)

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

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

打印出来:

一个dom地址
国际地址
邮政地址
一个互联网地址
x.400 地址

然而,在这种情况下,如果不是因为相当冗长的toDescriptiveString方法,枚举类不会interface Common比没有的更短。在向现有接口添加新功能时,接口中的默认方法将真正发挥作用,如果不破坏以前 Java 版本中接口的所有实现者,这是不可能的。

所有这些都基于尚未完成的Java SE 8 和 Lambda。您可以下载预发布版本,但请注意这是一项正在进行的工作。

于 2012-06-26T00:57:07.473 回答