【问题标题】:Why can't a class extend an enum?为什么类不能扩展枚举?
【发布时间】:2013-10-17 17:28:06
【问题描述】:

我想知道为什么在 Java 语言中 class 不能扩展 enum

我不是在谈论扩展 enumenum(这是无法做到的,因为 java 没有多重继承,并且 enums 隐式扩展 java.lang.Enum),而是extendsenum 的类,以便只添加额外的方法,而不是额外的枚举值。

类似:

enum MyEnum
{
    ASD(5),
    QWE(3),
    ZXC(7);
    private int number;
    private asd(int number)
    {
        this.number=number;
    }
    public int myMethod()
    {
        return this.number;
    }
}

class MyClass extends MyEnum
{
    public int anotherMethod()
    {
        return this.myMethod()+1;
    }
}

这样使用:

System.out.println(MyClass.ASD.anotherMethod());

那么,任何人都可以为此限制提供理由(或指出正确的 JLS 部分)吗?

【问题讨论】:

  • 因为enums 默认为final。您不能从 final 类扩展。
  • 我想他想知道“为什么它们是最终的 - 有没有理由它们必须或应该是最终的”
  • @user949300 这很容易通过使用 google 之类的 SEO =\ 找到
  • 为什么不直接编写一个采用MyEnum 参数的静态方法呢?
  • @LouisWasserman 是的,有很多解决方法,但我很好奇为什么选择它不允许按照我展示的方式进行操作:)

标签: java inheritance syntax enums jls


【解决方案1】:

您不能扩展enum。它们隐含为final。来自JLS § 8.9

枚举类型隐含为final,除非它包含至少一个具有类主体的枚举常量。

另外,来自JLS §8.1.4 - Superclasses and Subclasses

如果ClassType 将类命名为Enum 或对其的任何调用,则会出现编译时错误。

基本上,enum 是一组枚举的预定义常量。因此,该语言允许您在 switch-cases 中使用枚举。例如,通过允许扩展它们,不会使它们成为 switch-case 的合格类型。除此之外,扩展枚举的类或其他枚举的实例也将是您扩展的枚举的实例。这基本上破坏了enums 的目的。

【讨论】:

  • 是的,但我想知道为什么:你能提供一个理由让他们隐含final吗?我的意思是,如果他们不这样做会更好:)
  • @UnaiVivi 因为规范是这么说的?
  • @LuiggiMendoza 当然可以,否则它会编译。为什么规范会这样说?在编写 JLS 时,他们并没有随意选择,大多数(如果不是全部)决定都有充分的理由;这就是为什么我要询问其背后的理由。
  • @LuiggiMendoza 对不起,他已经收到了两个很好的理由,而不是意见,为什么事情是这样设计的。这是上帝(即高斯林)在创造宇宙时没有选择的情况。 (谷歌爱因斯坦原始报价)
  • @user949300 从 Java 7 开始,您还可以在普通的 Strings 上使用 switch,所以 switch 是 IMO 失败的原因。此外,enums 是定义常量,不应扩展或修改常量,IMO 这是拥有枚举的更强有力的理由。
【解决方案2】:

在 Java 1.5 之前的古代,你可能会做这样的枚举:

public class MyEnum {

public static final MyEnum ASD = new MyEnum(5);
public static final MyEnum QWE = new MyEnum(3);
public static final MyEnum ZXC = new MyEnum(7);

private int number;

private MyEnum(int number) {
    this.number = number;
}

public int myMethod() {
    return this.number;
    }

} 

这个图有两点很重要:

  • 不允许从外部实例化类的私有构造函数
  • 实际的“枚举”值存储在静态字段中

即使当你扩展它时它不是最终的,你也会意识到编译器需要一个显式的构造函数,而反过来又需要调用超级构造函数,这是不可能的,因为它是私有的。另一个问题是超类中的静态字段仍然存储该超类的对象,而不是您的扩展对象。我认为这可能是一个解释。

【讨论】:

    【解决方案3】:

    枚举的全部意义在于创建一个可能值的封闭集。这使得推断该枚举类型的值是什么变得更容易——对程序员来说更容易,对编译器也更容易(例如,这种封闭性使它能够在开关中有效地处理枚举)。允许一个类扩展一个枚举将打开一组可能的值;到那时,enum 能给你带来什么,而普通的class 不会?

    【讨论】:

    • 好吧,在我想象的 JLS 中,classes 扩展枚举无法添加额外的枚举值(仅由实际的 enum 提供)因此常量集仍将保持关闭和未扩展,但是然后枚举将再次携带扩展类提供的额外方法。
    • 好吧,您可以在枚举中定义抽象方法,并让每个枚举值都是实现该方法的匿名类。但是如果你想要一个命名类,你将如何在不扩展 enum 值的情况下实例化它?
    【解决方案4】:

    我认为他们为什么这样做的答案来自这个问题:

    在您的示例中,您将如何实例化 MyClass?枚举永远不会被用户显式实例化(通过new MyEnum())。您必须执行MyClass.ASD 之类的操作,但不确定它是如何工作的。

    基本上,我不知道哪种语法适用于您提议的添加。这可能就是为什么他们将它们定为最终等...

    编辑添加

    如果原始 Enum 的作者提前计划(不太可能),并且您不太担心线程安全,您可以执行以下操作:(顺便说一句,我可能会对实际在生产中这样做的任何人大喊大叫代码,YMMV)

    public enum ExtendibleEnum {
    
       FOO, BAR, ZXC;
    
       private Runnable anotherMethodRunme;  // exact Interface will vary, I picked an easy one
                               // this is what gets "injected" by your other class
    
       public void setAnotherMethodRunMe(Runnable r) {  // inject here
          anotherMethodRunme= r;
       }
    
       public void anotherMethod() {  // and this behavior gets changed
          anotherMethodRunme.run();
       }
    }
    

    【讨论】:

    • 我正要让你的答案成为公认的答案......但后来我看到你的编辑,有点尖叫^_^
    • ...但现在多亏了你,我想到了函数指针和传递代码块的想法:)
    • 我通常对此类内容非常克制,但我一直在阅读并享受“制作 Java Groovy”。所以我责怪这让我最近变得更加“实用”...... :-)
    【解决方案5】:
    【解决方案6】:

    枚举的全部意义在于创建一组封闭的可能值。这使得更容易推断该枚举类型的值是什么,因此常量集仍将保持封闭和未扩展,但枚举将再次携带扩展类提供的额外方法。

    【讨论】:

      猜你喜欢
      • 2011-12-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-07-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多