【发布时间】:2009-10-01 17:22:16
【问题描述】:
浏览Java源代码,发现如下声明:
public abstract class Enum<E extends Enum<E>>
应该如何解释?我被它困住了......
谢谢。
【问题讨论】:
-
感谢大家提供的所有令人印象深刻的意见。我认为每个答案都很有趣(所以我给了你一个 +1):)
-
看起来像 Java 版本的
C++'s CRTP
浏览Java源代码,发现如下声明:
public abstract class Enum<E extends Enum<E>>
应该如何解释?我被它困住了......
谢谢。
【问题讨论】:
C++'s CRTP
你并不孤单。 Ken Arnold 有 this 说:
或者,为了简要说明同一点, 考虑一下:枚举实际上是一个 泛型类定义为 Enum
>。你想 出来。我们放弃了解释 它。
(来自博客条目 Generics Considered Harmful)
【讨论】:
就像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<E>(它必须因为前一点)那么你已经确保Foo模板有它的子类的“意识”,因为它的子类作为模板参数传递给它Foo 的方法可以安全地将this 指针向下转换为它的派生子类E。 【讨论】:
class A extends Foo<A> 和class B extends Foo<A>。然后在B 内部,尝试将this(B 的实例)转换为参数(A)可能会导致类转换异常。
class B 在您指出的情况下没有按预期使用模板。 (即参数与派生的子类不匹配。)
C++'s CRTP
使用有界类型的原因 > 可能是由 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
【讨论】:
public static void use(Foo<E> foo) 和 public static void use(Bar<E> bar) 不按书面形式编译(你试过吗?)。它们不在E的范围内
E 是Enum 的直接(通常是具体的)子类,与 Comparable(Enum 实现 Comparable<E> 而不是 Comparable<Enum>)和其他一些方法一起使用。它这样做
访问实际的子类,我怀疑它也需要一些内部实现。
【讨论】:
Enum 类需要一个参数化类型 E,它是 Enum 的子类。
compareTo(E o) 等方法需要类型信息 E,它在类声明期间需要类型信息(例如 Comparable)。
Java编译器在你创建枚举类的时候会自动传入类型信息,所以你声明的时候是看不到的
enum MyType {...}
有些东西我不喜欢通用的使用方式。例如,当接口只需要类信息时,为什么我们需要详细地将类类型传递给接口?我们不能有默认值,还是编译器现在不够聪明?
例如
class String implements Comparable<String>
【讨论】:
这个类不是枚举类型。它只是一个复杂的通用常规类。很难说(没有看到整个代码)为什么它是这样的设计。但我猜想,当您希望方法始终返回当前类型时,它可能与 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();
}
}
这又与枚举数类型无关。
只是一个想法;
【讨论】:
public abstract class Enum<E> 也适用于您在上面所做的事情
您在代码中创建的所有枚举都将由扩展 Enum 类的最终类创建。
public enum MyEnum { XYZ }
会变成
public final class MyEnum extends Enum<MyEnum>
或类似的东西(不确定 XYZ 是成为实例还是扩展它的类 - 我认为它不是 真的 final,而是编译器不会让你扩展枚举)。 .. 无论如何,这样的枚举并不是真正有用的,因为你不能(不应该)真的用它自己“做”任何事情。
阅读它的 javadoc/code 以更好地理解你可以(不)用你的枚举做什么仍然是间接有用的。
【讨论】: