171

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

class Enum<E extends Enum<E>>

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

4

7 回答 7

111

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

public class StatusCode extends Enum<StatusCode>

现在,如果您检查约束,我们得到了Enum<StatusCode>-so E=StatusCode。让我们检查一下:EextendEnum<StatusCode>吗?是的!我们没事。

您可能会问自己这样做的意义何在 :) 好吧,这意味着 Enum 的 API 可以引用自身 - 例如,能够说Enum<E>implements 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 would implement Comparable<First> rather than Comparable<Second>... but First itself would be fine.

于 2008-10-17T05:30:53.680 回答
30

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)

于 2009-04-17T00:31:29.497 回答
8

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!
    }
}
于 2013-11-27T22:04:07.523 回答
3

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”.

于 2008-10-17T05:32:45.967 回答
1

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(){...}
}
于 2009-10-13T18:52:04.857 回答
1

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.

于 2015-06-05T13:34:21.813 回答
0

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.

于 2019-12-17T06:42:26.263 回答