Java Enum定义

时间:2008-10-17 05:14:16

标签: java generics enums crtp

我认为我非常了解Java泛型,但后来我在java.lang.Enum中遇到了以下内容:

class Enum<E extends Enum<E>>

有人可以解释如何解释此类型参数吗?奖励点用于提供可以使用类似类型参数的其他示例。

7 个答案:

答案 0 :(得分:103)

这意味着枚举的类型参数必须从枚举中派生,枚举本身具有相同的类型参数。怎么会发生这种情况?通过使类型参数成为新类型本身。因此,如果我有一个名为StatusCode的枚举,它将等同于:

public class StatusCode extends Enum<StatusCode>

现在,如果您检查约束,我们已经Enum<StatusCode> - 所以E=StatusCode。我们来检查:E是否Enum<StatusCode>?是!我们没事。

你可能会问自己这是什么意思:)嗯,这意味着Enum的API可以引用自身 - 例如,能够说Enum<E>实现Comparable<E> 。基类能够进行比较(在枚举的情况下),但它可以确保它只比较正确的枚举类型。 (编辑:嗯,差不多 - 看看底部的编辑。)

我在ProtocolBuffers的C#端口使用了类似的东西。有“消息”(不可变)和“构建器”(可变,用于构建消息) - 它们是成对的类型。涉及的接口是:

public interface IBuilder<TMessage, TBuilder>
  where TMessage : IMessage<TMessage, TBuilder> 
  where TBuilder : IBuilder<TMessage, TBuilder>

public interface IMessage<TMessage, TBuilder>
  where TMessage : IMessage<TMessage, TBuilder> 
  where TBuilder : IBuilder<TMessage, TBuilder>

这意味着从消息中您可以获得适当的构建器(例如,获取消息的副本并更改某些位),并且从构建器中,您可以在完成构建后获得相应的消息。虽然API的用户不需要真正关心它,但这是一个很好的工作 - 它非常复杂,需要多次迭代才能到达目的地。

编辑:请注意,这并不能阻止您创建使用类型参数的奇数类型,这种类型参数本身可以,但它的类型不同。目的是在正确的案例中提供好处,而不是保护您免受错误的案件的侵害。

因此,如果Enum无法在Java中“特别”处理,您可以(如评论中所述)创建以下类型:

public class First extends Enum<First> {}
public class Second extends Enum<First> {}

Second会实施Comparable<First>而不是Comparable<Second> ......但First本身就没问题。

答案 1 :(得分:26)

以下是 Java Generics and Collections 一书中解释的修改版本: 我们宣布了Enum

enum Season { WINTER, SPRING, SUMMER, FALL }

将扩展为类

final class Season extends ...

其中...是Enums的以某种方式参数化的基类。开始工作吧 这是必须的。好吧,Season的一个要求是它应该实现Comparable<Season>。所以我们需要

Season extends ... implements Comparable<Season>

您可以使用哪些...来实现此功能?鉴于它必须是Enum的参数化,唯一的选择是Enum<Season>,以便您可以:

Season extends Enum<Season>
Enum<Season> implements Comparable<Season>

所以Enum的参数设置类似于Season。摘自Season和。{ 你知道Enum的参数是任何满足

的类型
 E extends Enum<E>

Maurice Naftalin(共同作者,Java Generics and Collections)

答案 2 :(得分:5)

这可以通过一个简单的例子和​​技术来说明,该例子和技术可用于实现子类的链式方法调用。在下面的示例中,setName会返回Node,因此链接不适用于City

class Node {
    String name;

    Node setName(String name) {
        this.name = name;
        return this;
    }
}

class City extends Node {
    int square;

    City setSquare(int square) {
        this.square = square;
        return this;
    }
}

public static void main(String[] args) {
    City city = new City()
        .setName("LA")
        .setSquare(100);    // won't compile, setName() returns Node
}

因此我们可以在泛型声明中引用一个子类,以便City现在返回正确的类型:

abstract class Node<SELF extends Node<SELF>>{
    String name;

    SELF setName(String name) {
        this.name = name;
        return self();
    }

    protected abstract SELF self();
}

class City extends Node<City> {
    int square;

    City setSquare(int square) {
        this.square = square;
        return self();
    }

    @Override
    protected City self() {
        return this;
    }

    public static void main(String[] args) {
       City city = new City()
            .setName("LA")
            .setSquare(100);                 // ok!
    }
}

答案 3 :(得分:2)

你不是唯一想知道这意味着什么的人;见Chaotic Java blog

“如果一个类扩展了这个类,它应该传递一个参数E.参数E的边界是一个用相同的参数E”扩展这个类的类。

答案 4 :(得分:2)

这篇文章完全向我阐明了“递归泛型类型”的问题。 我只想添加另一个需要这种特殊结构的案例。

假设您在通用图表中有通用节点:

public abstract class Node<T extends Node<T>>
{
    public void addNeighbor(T);

    public void addNeighbors(Collection<? extends T> nodes);

    public Collection<T> getNeighbor();
}

然后你可以得到专门类型的图表:

public class City extends Node<City>
{
    public void addNeighbor(City){...}

    public void addNeighbors(Collection<? extends City> nodes){...}

    public Collection<City> getNeighbor(){...}
}

答案 5 :(得分:1)

如果您查看Enum源代码,则会有以下内容:

public abstract class Enum<E extends Enum<E>>
        implements Comparable<E>, Serializable {

    public final int compareTo(E o) {
        Enum<?> other = (Enum<?>)o;
        Enum<E> self = this;
        if (self.getClass() != other.getClass() && // optimization
            self.getDeclaringClass() != other.getDeclaringClass())
            throw new ClassCastException();
        return self.ordinal - other.ordinal;
    }

    @SuppressWarnings("unchecked")
    public final Class<E> getDeclaringClass() {
        Class<?> clazz = getClass();
        Class<?> zuper = clazz.getSuperclass();
        return (zuper == Enum.class) ? (Class<E>)clazz : (Class<E>)zuper;
    }

    public static <T extends Enum<T>> T valueOf(Class<T> enumType,
                                                String name) {
        T result = enumType.enumConstantDirectory().get(name);
        if (result != null)
            return result;
        if (name == null)
            throw new NullPointerException("Name is null");
        throw new IllegalArgumentException(
            "No enum constant " + enumType.getCanonicalName() + "." + name);
    } 
}

首先,E extends Enum<E>是什么意思?这意味着类型参数是从Enum扩展的,并且没有使用原始类型进行参数化(它本身是参数化的。)

如果您有枚举

,这是相关的
public enum MyEnum {
    THING1,
    THING2;
}

如果我知道的话,将被翻译为

public final class MyEnum extends Enum<MyEnum> {
    public static final MyEnum THING1 = new MyEnum();
    public static final MyEnum THING2 = new MyEnum();
}

所以这意味着MyEnum会收到以下方法:

public final int compareTo(MyEnum o) {
    Enum<?> other = (Enum<?>)o;
    Enum<MyEnum> self = this;
    if (self.getClass() != other.getClass() && // optimization
        self.getDeclaringClass() != other.getDeclaringClass())
        throw new ClassCastException();
    return self.ordinal - other.ordinal;
}

更重要的是,

    @SuppressWarnings("unchecked")
    public final Class<MyEnum> getDeclaringClass() {
        Class<?> clazz = getClass();
        Class<?> zuper = clazz.getSuperclass();
        return (zuper == Enum.class) ? (Class<MyEnum>)clazz : (Class<MyEnum>)zuper;
    }

这会使getDeclaringClass()转换为正确的Class<T>对象。

一个更清晰的例子就是我在this question上回答的一个例子,如果你想指定一个通用的边界,你就无法避免这种结构。

答案 6 :(得分:0)

根据维基百科,此模式称为Curiously recurring template pattern。 基本上,通过使用CRTP模式,我们可以轻松地引用子类类型而无需类型转换,这意味着通过使用模式,我们可以模仿虚函数。