Java 枚举定义

发布于 2024-07-07 02:27:48 字数 178 浏览 14 评论 0原文

我以为我对 Java 泛型了解得很好,但后来我在 java.lang.Enum 中遇到了以下内容:

class Enum<E extends Enum<E>>

有人能解释一下如何解释这个类型参数吗? 提供可以使用类似类型参数的其他示例的奖励积分。

I thought I understood Java generics pretty well, but then I came across the following in java.lang.Enum:

class Enum<E extends Enum<E>>

Could someone explain how to interpret this type parameter? Bonus points for providing other examples of where a similar type parameter could be used.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

桃扇骨 2024-07-14 02:27:48

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

public class StatusCode extends Enum<StatusCode>

现在,如果您检查约束,我们就会得到 Enum - 所以 E=StatusCode。 让我们检查一下:E 是否扩展了 Enum? 是的! 我们没事。

您可能会问自己这有什么意义:) 好吧,这意味着 Enum 的 API 可以引用自身 - 例如,可以说 Enum 实现 可比较。 基类能够进行比较(在枚举的情况下),但它可以确保它只比较正确类型的枚举。 (编辑:嗯,几乎 - 请参阅底部的编辑。)

我在 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; 而不是 Comparable...但是 First 本身就可以了。

It means that the type argument for enum has to derive from an enum which itself has the same type argument. How can this happen? By making the type argument the new type itself. So if I've got an enum called StatusCode, it would be equivalent to:

public class StatusCode extends Enum<StatusCode>

Now if you check the constraints, we've got Enum<StatusCode> - so E=StatusCode. Let's check: does E extend Enum<StatusCode>? Yes! We're okay.

You may well be asking yourself what the point of this is :) Well, it means that the API for Enum can refer to itself - for instance, being able to say that Enum<E> implements Comparable<E>. The base class is able to do the comparisons (in the case of enums) but it can make sure that it only compares the right kind of enums with each other. (EDIT: Well, nearly - see the edit at the bottom.)

I've used something similar in my C# port of ProtocolBuffers. There are "messages" (immutable) and "builders" (mutable, used to build a message) - and they come as pairs of types. The interfaces involved are:

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>

This means that from a message you can get an appropriate builder (e.g. to take a copy of a message and change some bits) and from a builder you can get an appropriate message when you've finished building it. It's a good job users of the API don't need to actually care about this though - it's horrendously complicated, and took several iterations to get to where it is.

EDIT: Note that this doesn't stop you from creating odd types which use a type argument which itself is okay, but which isn't the same type. The purpose is to give benefits in the right case rather than protect you from the wrong case.

So if Enum weren't handled "specially" in Java anyway, you could (as noted in comments) create the following types:

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

Second would implement Comparable<First> rather than Comparable<Second>... but First itself would be fine.

爱格式化 2024-07-14 02:27:48

以下是《Java Generics and Collections》一书中的解释的修改版本:
我们声明了一个 Enum

enum Season { WINTER, SPRING, SUMMER, FALL }

,它将扩展为一个类

final class Season extends ...

,其中 ... 将成为 Enum 的参数化基类。 开始工作吧
弄清楚那必须是什么。 那么,Season 的要求之一是它应该实现Comparable。 所以我们需要

Season extends ... implements Comparable<Season>

你可以使用什么...来让这个工作? 鉴于它必须是 Enum 的参数化,唯一的选择是 Enum,这样您就可以:

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

所以 Enum 是在Season 等类型上进行参数化。 摘要来自 Season
您会发现 Enum 的参数是满足

 E extends Enum<E>

Maurice Naftalin(Java Generics and Collections 合著者)要求的任何类型

The following is a modified version of the explanation from the book Java Generics and Collections:
We have an Enum declared

enum Season { WINTER, SPRING, SUMMER, FALL }

which will be expanded to a class

final class Season extends ...

where ... is to be the somehow-parameterised base class for Enums. Let's work
out what that has to be. Well, one of the requirements for Season is that it should implement Comparable<Season>. So we're going to need

Season extends ... implements Comparable<Season>

What could you use for ... that would allow this to work? Given that it has to be a parameterisation of Enum, the only choice is Enum<Season>, so that you can have:

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

So Enum is parameterised on types like Season. Abstract from Season and
you get that the parameter of Enum is any type that satisfies

 E extends Enum<E>

Maurice Naftalin (co-author, Java Generics and Collections)

失眠症患者 2024-07-14 02:27:48

这可以通过一个简单的示例和一种可用于实现子类的链式方法调用的技术来说明。 在下面的示例中,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!
    }
}

This can be illustrated by a simple example and a technique which can be used to implement chained method calls for sub-classes. In an example below setName returns a Node so chaining won't work for the 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
}

So we could reference a sub-class in a generic declaration, so that the City now returns the correct type:

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!
    }
}
戏蝶舞 2024-07-14 02:27:48

您并不是唯一一个想知道这意味着什么的人;您也知道这一点。 请参阅 Chaotic Java 博客

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

You are not the only one wondering what that means; see Chaotic Java blog.

“If a class extends this class, it should pass a parameter E. The parameter E’s bounds are for a class which extends this class with the same parameter E”.

木森分化 2024-07-14 02:27:48

这篇文章完全向我澄清了“递归泛型类型”的问题。
我只是想添加另一个需要这种特殊结构的情况。

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

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(){...}
}

This post has totally clarified to me these problem of 'recursive generic types'.
I just wanted to add another case where this particular structure is necessary.

Suppose you have generic nodes in a generic graph:

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

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

    public Collection<T> getNeighbor();
}

Then you can have graphs of specialized types:

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

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

    public Collection<City> getNeighbor(){...}
}
过度放纵 2024-07-14 02:27:48

如果你看一下 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 是什么意思? 这意味着类型参数是从 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();
}

So 这意味着 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() 转换为正确的 < code>Class对象。

一个更清晰的例子是我在这个问题< /a> 如果您想指定通用边界,则无法避免此构造。

If you look at the Enum source code, it has the following:

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

First thing first, what does E extends Enum<E> mean? It means the type parameter is something that extends from Enum, and isn't parametrized with a raw type (it's parametrized by itself).

This is relevant if you have an enum

public enum MyEnum {
    THING1,
    THING2;
}

which, if I know correctly, is translated to

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

So this means that MyEnum receives the following methods:

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

And even more importantly,

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

This makes getDeclaringClass() cast to the proper Class<T> object.

A way clearer example is the one that I answered on this question where you cannot avoid this construct if you want to specify a generic bound.

戴着白色围巾的女孩 2024-07-14 02:27:48

根据维基百科,这种模式被称为奇怪的重复模板模式
基本上,通过使用CRTP模式,我们可以轻松地引用子类类型而无需类型转换,这意味着通过使用该模式,我们可以模仿虚函数。

According to wikipedia, this pattern is called Curiously recurring template pattern.
Basically, by using the CRTP pattern, we can easily refer to subclass type without type casting, which means by using the pattern, we can imitate virtual function.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文