Definición de enumeración de Java


Pensé que entendía Java generics bastante bien, pero luego me encontré con lo siguiente en java.lang.Enum:

class Enum<E extends Enum<E>>

¿Podría alguien explicar cómo interpretar este parámetro de tipo? Puntos de bonificación por proporcionar otros ejemplos de dónde se podría usar un parámetro de tipo similar.

Author: CajunLuke, 2008-10-17

7 answers

Significa que el argumento de tipo para enum tiene que derivar de una enum que a su vez tiene el mismo argumento de tipo. ¿Cómo puede pasar esto? Haciendo que el argumento type sea el nuevo tipo en sí. Así que si tengo una enumeración llamada statusCode, sería equivalente a:

public class StatusCode extends Enum<StatusCode>

Ahora, si compruebas las restricciones, tenemos Enum<StatusCode> - so E=StatusCode. Veamos: ¿se extiende E Enum<StatusCode>? Sí! Estamos bien.

Puede que te estés preguntando cuál es el punto de esto:) Bueno, significa que la API para Enum puede referirse a sí mismo-por ejemplo, ser capaz de decir que Enum<E> implementa Comparable<E>. La clase base es capaz de hacer las comparaciones (en el caso de enums) pero puede asegurarse de que solo compara el tipo correcto de enums entre sí. (EDITAR: Bueno, casi-ver la edición en la parte inferior.)

He usado algo similar en mi puerto C# de ProtocolBuffers. Hay "mensajes" (inmutables) y" constructores " (mutables, usados para construir un mensaje) - y vienen como pares de tipos. Interfaz los participantes son:

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>

Esto significa que de un mensaje puedes obtener un constructor apropiado (por ejemplo, para tomar una copia de un mensaje y cambiar algunos bits) y de un constructor puedes obtener un mensaje apropiado cuando hayas terminado de construirlo. Es un buen trabajo que los usuarios de la API no necesitan realmente preocuparse por esto , sin embargo, es terriblemente complicado, y tomó varias iteraciones para llegar a donde está.

EDITAR: Tenga en cuenta que esto no le impide crear tipos impares que usan un tipo argumento que en sí mismo está bien, pero que no es el mismo tipo. El propósito es dar beneficios en el caso correcto en lugar de protegerte del caso incorrecto.

Así que si Enum no se manejó "especialmente" en Java de todos modos, podría (como se indica en los comentarios) crear los siguientes tipos:

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

Second implementar Comparable<First> en lugar de Comparable<Second>... pero First sí mismo estaría bien.

 98
Author: Jon Skeet,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-05-29 12:25:30

La siguiente es una versión modificada de la explicación del libro Java Generics and Collections : Tenemos unEnum declarado

enum Season { WINTER, SPRING, SUMMER, FALL }

Que se expandirá a una clase

final class Season extends ...

Donde ... debe ser la clase base de alguna manera parametrizada para Enumeraciones. Vamos a trabajar lo que tiene que ser. Bueno, uno de los requisitos para Season es que debe implementar Comparable<Season>. Así que vamos a necesitar

Season extends ... implements Comparable<Season>

¿Qué podría usar para ... que permitiría esto ¿a trabajar? Dado que tiene que ser una parametrización de Enum, la única opción es Enum<Season>, por lo que puede tener:

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

Así que Enum se parametriza en tipos como Season. Resumen de Season y se obtiene que el parámetro de Enum es cualquier tipo que satisface

 E extends Enum<E>

Maurice Naftalin (coautor, Java Generics and Collections)

 25
Author: Maurice Naftalin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-12-14 23:59:19

Esto se puede ilustrar con un ejemplo simple y una técnica que se puede usar para implementar llamadas de método encadenado para subclases. En un ejemplo a continuación setName devuelve un Node por lo que el encadenamiento no funcionará para el 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
}

Así que podríamos hacer referencia a una subclase en una declaración genérica, de modo que el City ahora devuelve el tipo correcto:

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
Author: Andrey Chaschev,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-11-30 09:15:29

Usted no es el único que se pregunta lo que eso significa; ver Blog Java caótico.

"Si una clase extiende esta clase, debe pasar un parámetro E. Los límites del parámetro E son para una clase que extiende esta clase con el mismo parámetro E".

 2
Author: kpirkkal,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-10-17 05:32:45

Este post me ha aclarado totalmente estos problemas de 'tipos genéricos recursivos'. Sólo quería añadir otro caso en el que esta estructura particular es necesaria.

Supongamos que tiene nodos genéricos en un gráfico genérico:

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

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

    public Collection<T> getNeighbor();
}

Entonces usted puede tener gráficos de tipos especializados:

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

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

    public Collection<City> getNeighbor(){...}
}
 2
Author: nozebacle,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-10-14 07:08:38

En el caso de Enum, es inútil. Todo funcionaría igual si se declarara como

class Enum<E>
 0
Author: newacct,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-05-29 08:26:17

Si nos fijamos en el código fuente Enum, tiene lo siguiente:

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

Lo primero es lo primero, ¿qué significa E extends Enum<E>? Significa que el parámetro type es algo que se extiende desde Enum, y no está parametrizado con un tipo raw (está parametrizado por sí mismo).

Esto es relevante si tiene una enumeración

public enum MyEnum {
    THING1,
    THING2;
}

Que, si lo sé correctamente, se traduce a

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

Esto significa que MyEnum recibe los siguientes métodos:

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

Y aún más es importante destacar,

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

Esto hace que getDeclaringClass() se lance al objeto Class<T> apropiado.

Un ejemplo mucho más claro es el que respondí en esta pregunta donde no puedes evitar esta construcción si quieres especificar un enlace genérico.

 0
Author: EpicPandaForce,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:34:28