example - Definición de Java Enum
java enum valueof (7)
En el caso de Enum
, es inútil. Todo funcionaría igual si fuera declarado como
class Enum<E>
Creí entender los genéricos de Java 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 podría usarse un parámetro de tipo similar.
Esta publicación me ha aclarado totalmente el problema de los "tipos genéricos recursivos". Solo quería agregar otro caso donde 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 puedes 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(){...}
}
Esto se puede ilustrar con un ejemplo simple y una técnica que se puede usar para implementar llamadas a métodos encadenados para subclases. En un ejemplo a continuación setName
devuelve un Node
para que el encadenamiento no funcione para la 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
}
Entonces, podríamos hacer referencia a una subclase en una declaración genérica, de modo que la City
ahora devuelva 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!
}
}
La siguiente es una versión modificada de la explicación del libro Java Generics and Collections : Tenemos un Enum
declarado
enum Season { WINTER, SPRING, SUMMER, FALL }
que se ampliará a una clase
final class Season extends ...
donde ...
debe ser la clase base parametrizada de algún modo para Enums. Vamos a ver qué tiene que ser eso. Bueno, uno de los requisitos de Season
es que debería implementar Comparable<Season>
. Entonces vamos a necesitar
Season extends ... implements Comparable<Season>
¿Qué podrías usar para ...
eso permitiría que esto funcione? Dado que tiene que ser una parametrización de Enum
, la única opción es Enum<Season>
, para que pueda tener:
Season extends Enum<Season>
Enum<Season> implements Comparable<Season>
Entonces Enum
está parametrizado en tipos como Season
. Resumen de Season
y obtienes que el parámetro de Enum
es de cualquier tipo que satisfaga
E extends Enum<E>
Maurice Naftalin (coautor, Java Generics and Collections)
Si miras el código fuente de 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);
}
}
Primero lo primero, ¿qué significa E extends Enum<E>
? Significa que el parámetro de tipo es algo que se extiende desde Enum, y no está parametrizado con un tipo crudo (está parametrizado por sí mismo).
Esto es relevante si tienes 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 importante,
@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()
al objeto apropiado de la Class<T>
.
Un ejemplo mucho más claro es el que respondí en esta pregunta donde no se puede evitar este constructo si se desea especificar un límite genérico.
Significa que el argumento de tipo para enum tiene que derivar de una enumeración que tiene el mismo argumento de tipo. ¿Cómo puede pasar esto? Al hacer que el argumento de tipo sea el nuevo tipo en sí mismo. Entonces, si tengo una enumeración llamada StatusCode, sería equivalente a:
public class StatusCode extends Enum<StatusCode>
Ahora, si comprueba las restricciones, tenemos Enum<StatusCode>
- entonces E=StatusCode
. Comprobamos: ¿ E
extiende Enum<StatusCode>
? ¡Sí! Estamos bien
Es posible que se pregunte qué sentido tiene esto: bueno, significa que la API para Enum puede referirse a sí misma, por ejemplo, poder decir que Enum<E>
implementa Comparable<E>
. La clase base puede hacer las comparaciones (en el caso de las enumeraciones), pero puede asegurarse de que solo compara el tipo correcto de enumeraciones 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, utilizados para crear un mensaje), y vienen en pares de tipos. Las interfaces involucradas 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 a partir de un mensaje puede obtener un constructor apropiado (por ejemplo, para tomar una copia de un mensaje y cambiar algunos bits) y desde un constructor puede obtener un mensaje apropiado cuando haya terminado de compilarlo. Sin embargo, es un buen trabajo que los usuarios de la API no tengan que preocuparse por esto realmente: es horrendo y complicado, y tomó varias iteraciones para llegar a donde está.
EDITAR: Tenga en cuenta que esto no le impide crear tipos impares que utilizan un argumento de tipo que, en sí mismo, está bien, pero que no es del mismo tipo. El objetivo es brindar beneficios en el caso correcto en lugar de protegerlo del caso incorrecto .
Entonces, si Enum
no se manejó "especialmente" en Java de todos modos, podría (como se señala en los comentarios) crear los siguientes tipos:
public class First extends Enum<First> {}
public class Second extends Enum<First> {}
Second
implementaría Comparable<First>
lugar de Comparable<Second>
... pero First
sí estaría bien.
Usted no es el único que se pregunta qué significa eso; ver el blog Chaotic Java .
"Si una clase extiende esta clase, debe pasar un parámetro E. Los límites del parámetro E son para una clase que amplía esta clase con el mismo parámetro E".