【问题标题】:What is the meaning of the Java's Enum declaration?Java 的 Enum 声明是什么意思?
【发布时间】:2009-10-01 17:22:16
【问题描述】:

浏览Java源代码,发现如下声明:

public abstract class Enum<E extends Enum<E>>

应该如何解释?我被它困住了......

谢谢。

【问题讨论】:

  • 感谢大家提供的所有令人印象深刻的意见。我认为每个答案都很有趣(所以我给了你一个 +1):)
  • 看起来像 Java 版本的 C++'s CRTP

标签: java generics


【解决方案1】:

你并不孤单。 Ken Arnoldthis 说:

或者,为了简要说明同一点, 考虑一下:枚举实际上是一个 泛型类定义为 Enum>。你想 出来。我们放弃了解释 它。

(来自博客条目 Generics Considered Harmful

【讨论】:

  • 感谢您的链接。我很高兴我并不孤单:)
【解决方案2】:

就像quining! @LES2 在正确的轨道上。

public abstract class Foo <E extends Foo<E>> 
{

   public static void use(Foo<E> foo) {
      // use foo
   }
}

如果你有以下课程:

public class FooImpl extends Foo<FooImpl> {
  // ...
}

那么这些递归模板给你的魔力就是:

  • Foo 模板要求其参数扩展自身 (Foo)。
  • 如果参数类E,反过来,扩展Foo&lt;E&gt;(它必须因为前一点)那么你已经确保Foo模板有它的子类的“意识”,因为它的子类作为模板参数传递给它
  • 这反过来意味着Foo 的方法可以安全地将this 指针向下转换为它的派生子类E

【讨论】:

  • “这反过来意味着 Foo 的方法可以安全地将 this 指针向下转换为它的派生子类 E。”不对。例如,您可以拥有class A extends Foo&lt;A&gt;class B extends Foo&lt;A&gt;。然后在B 内部,尝试将thisB 的实例)转换为参数(A)可能会导致类转换异常。
  • 好吧,好吧,你指出了一个病态的案例; class B 在您指出的情况下没有按预期使用模板。 (即参数与派生的子类不匹配。)
  • @JasonS:没有所谓的“病态案例”。正确使用的泛型被证明是安全的。不安全的东西意味着对泛型的错误使用。泛型根本不能用于做你想做的事情。
  • 看起来像Java版本的C++'s CRTP
  • 基本上是的。
【解决方案3】:

使用有界类型的原因 > 可能是由 PECS 经验法则来解释的(Joshua Bloch 在 Effective Java 中解释过)。

PECS 代表“Producer, extends; Consumer super”,它是一个首字母缩写词,解释了在设计泛型方法时如何以及何时使用有界通配符。

让我们检查任何具有此签名的抽象类。

public abstract class Foo <E extends Foo<E>> {

   public static void use(Foo<E> foo) {
      // use foo
   }
}

还有一个不使用有界通配符的抽象类:

public abstract class Bar<E> {
   public static void use(Bar<E> bar)  {
      // use bar
   }
}

我们的具体类是:

public class FooImpl extends Foo<FooImpl> {
  // ...
}
public class AnotherFooImpl extends Foo<AnotherFooImpl> { ... }

public class BarImpl extends Bar<BarImpl> {
   ///
}

public class AnotherBarImpl extends Bar<AnotherBarImpl> { ... }

我们的主要程序是:

public class FooBar {
   public static void main(String[] args) {
      Foo.use(new FooImpl()); // works
      Foo.use(new AnotherFooImpl()); // works

      Bar.use(new BarImpl()); // doesn't work -- why?
      Bar.use(new AnotherBarImpl()); // doesn't work -- why?
   }
}

要使 Bar.use(new BarImpl()) 工作,必须使用通配符。

(我想 - 我的头顶 - 我还没有编译它,所以我希望我是对的 :)

每个枚举元素实际上都是枚举类型的子类:

enum Foo {
   FooImpl, AnotherFooImpl, ...;
}

在 Enum 基类中有一些方法需要确保它们具有正确类型的子类,并且要使其工作,该语法是必要的。

我希望这会有所帮助(如果你有时间试试这个例子)。

-- LES

【讨论】:

  • 嗨@LES2!感谢您的回答,但我无法编译 Foo,因为它说“使用”方法不能“对非静态类型 E 进行静态引用”。 Bar 类也是如此。
  • public static > void use(Foo foo) { // use foo } 你必须为静态方法复制泛型类型。对不起。
  • “它是一个首字母缩略词,解释了在设计泛型方法时如何以及何时使用有界通配符。”是的,这里没有通配符。
  • public static void use(Foo&lt;E&gt; foo)public static void use(Bar&lt;E&gt; bar) 不按书面形式编译(你试过吗?)。它们不在E的范围内
【解决方案4】:

EEnum 的直接(通常是具体的)子类,与 Comparable(Enum 实现 Comparable&lt;E&gt; 而不是 Comparable&lt;Enum&gt;)和其他一些方法一起使用。它这样做 访问实际的子类,我怀疑它也需要一些内部实现。

【讨论】:

    【解决方案5】:

    Enum 类需要一个参数化类型 E,它是 Enum 的子类。

    compareTo(E o) 等方法需要类型信息 E,它在类声明期间需要类型信息(例如 Comparable)。

    Java编译器在你创建枚举类的时候会自动传入类型信息,所以你声明的时候是看不到的

    enum MyType {...}
    

    有些东西我不喜欢通用的使用方式。例如,当接口只需要类信息时,为什么我们需要详细地将类类型传递给接口?我们不能有默认值,还是编译器现在不够聪明?

    例如

    class String implements Comparable<String>
    

    【讨论】:

      【解决方案6】:

      这个类不是枚举类型。它只是一个复杂的通用常规类。很难说(没有看到整个代码)为什么它是这样的设计。但我猜想,当您希望方法始终返回当前类型时,它可能与 self 类型的概念有关。

      
      public abstract class Enum<E extends Enum<E>> {
          E getMe() { return (E)this; }
      }
      public class E1 extends Enum<E1> {
          void doE2_only() {}
          void doE2() {
              // This line is to prove that Javac will see this.getMe() as a function of E1
              this.getMe().doE2_only();
          }
      }
      public class E2 extends Enum<E2> {
          void doE2_only() {}
          void doE2() {
              // This line is to prove that Javac will see this.getMe() as a function of E2
              this.getMe().doE2_only();
          }
      }
      

      这又与枚举数类型无关。

      只是一个想法;

      【讨论】:

      • 嗯。 “enum”关键字是在幕后使用类 Enum 实现的。
      • 经过一番调查,它似乎是你所说的枚举类型的后端。
      • 为什么与 java.lang.Enum 相同的构造无法编译? javac会阻止它吗?为什么会这样?
      • 对不起,“为什么与 java.lang.Enum 相同的构造”不能编译是什么意思?“你的意思是你创建了一个类似的结构类,它没有编译?我确实试过了(我发布的代码已编译并运行)。您能详细说明一下吗?
      • public abstract class Enum&lt;E&gt; 也适用于您在上面所做的事情
      【解决方案7】:

      您在代码中创建的所有枚举都将由扩展 Enum 类的最终类创建。

      public enum MyEnum { XYZ }
      

      会变成

      public final class MyEnum extends Enum<MyEnum>
      

      或类似的东西(不确定 XYZ 是成为实例还是扩展它的类 - 我认为它不是 真的 final,而是编译器不会让你扩展枚举)。 .. 无论如何,这样的枚举并不是真正有用的,因为你不能(不应该)真的用它自己“做”任何事情。

      阅读它的 javadoc/code 以更好地理解你可以(不)用你的枚举做什么仍然是间接有用的。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-09-20
        • 1970-01-01
        • 2022-12-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多