Las enumeraciones de Java son geniales. Así son los genéricos. Por supuesto, todos conocemos las limitaciones de este último debido al tipo de borrado. Pero hay una cosa que no entiendo, ¿por qué no puedo crear una enumeración como esta:
public enum MyEnum<T> {
LITERAL1<String>,
LITERAL2<Integer>,
LITERAL3<Object>;
}
Este parámetro de tipo genérico <T>
a su vez podría ser útil en varios lugares. Imagine un parámetro de tipo genérico para un método:
public <T> T getValue(MyEnum<T> param);
O incluso en la clase enum:
public T convert(Object o);
Ejemplo más concreto # 1
Dado que el ejemplo anterior puede parecer demasiado abstracto para algunos, aquí hay un ejemplo más real de por qué quiero hacer esto. En este ejemplo quiero usar
- Enums, porque entonces puedo enumerar un conjunto finito de claves de propiedad
- Genéricos, porque entonces puedo tener seguridad de tipos a nivel de método para almacenar propiedades
public interface MyProperties {
public <T> void put(MyEnum<T> key, T value);
public <T> T get(MyEnum<T> key);
}
Ejemplo más concreto # 2
Tengo una enumeración de tipos de datos:
public interface DataType<T> {}
public enum SQLDataType<T> implements DataType<T> {
TINYINT<Byte>,
SMALLINT<Short>,
INT<Integer>,
BIGINT<Long>,
CLOB<String>,
VARCHAR<String>,
...
}
Cada enum literal obviamente tendría propiedades adicionales basadas en el tipo genérico <T>
, al mismo tiempo que sería una enumeración (inmutable, singleton, enumerable, etc., etc.)
Pregunta:
¿Nadie pensó en esto? ¿Es esta una limitación relacionada con el compilador? Teniendo en cuenta el hecho de que la palabra clave " enum " se implementa como azúcar sintáctico, que representa el código generado para la JVM, no entiendo esta limitación.
¿Quién me puede explicar esto? Antes de responder, considere esto:
- Sé que los tipos genéricos se borran :-)
- Sé que hay soluciones alternativas al usar objetos de clase. Son soluciones alternativas.
- Los tipos genéricos resultan en conversiones de tipo generadas por el compilador cuando corresponda (por ejemplo, al llamar al método convert ()
- El tipo genérico <T> estaría en la enumeración. Por lo tanto, está obligado por cada uno de los literales de la enumeración. Por lo tanto, el compilador sabría qué tipo aplicar al escribir algo como
String string = LITERAL1.convert(myObject); Integer integer = LITERAL2.convert(myObject);
- Lo mismo se aplica al parámetro de tipo genérico en el
T getvalue()
método. El compilador puede aplicar la conversión de tipos al llamarString string = someClass.getValue(LITERAL1)
enum
conviértalo en un lenguaje de "enumeración segura de tipos" que usamos antes de Java 1.5. De repente, puede tener sus miembros de enumeración parametrizados. Eso es probablemente lo que voy a hacer ahora.Respuestas:
Esto ahora se está discutiendo a partir de las enumeraciones mejoradas JEP-301 . El ejemplo dado en el JEP es, que es precisamente lo que estaba buscando:
Desafortunadamente, el JEP todavía está luchando con problemas importantes: http://mail.openjdk.java.net/pipermail/amber-spec-experts/2017-May/000041.html
fuente
La respuesta está en la pregunta:
Ninguno de estos dos métodos es posible, ya que el tipo de argumento se borra.
Sin embargo, para realizar esos métodos, podría construir su enumeración como:
fuente
public T convert(Object);
. Supongo que este método podría, por ejemplo, limitar un montón de tipos diferentes a <T>, que por ejemplo es una Cadena. La construcción del objeto String es tiempo de ejecución, nada que haga el compilador. Por lo tanto, debe conocer el tipo de tiempo de ejecución, como String.class. ¿O me estoy perdiendo algo?public final static String FOO;
con un bloque estáticostatic { FOO = "bar"; }
: también se evalúan las constantes incluso cuando se conocen en tiempo de compilación.Porque no puedes Seriamente. Eso podría agregarse a la especificación del idioma. No ha sido Agregaría algo de complejidad. Ese beneficio para el costo significa que no es una alta prioridad.
Actualización: Actualmente se está agregando al lenguaje bajo JEP 301: Enums mejorados .
fuente
Hay otros métodos en ENUM que no funcionarían. ¿Qué
MyEnum.values()
volvería?¿Qué hay de
MyEnum.valueOf(String name)
?Por el valor de si crees que el compilador podría hacer un método genérico como
para llamarlo así
MyEnum<String> myStringEnum = MyEnum.value("some string property")
, tampoco funcionaría. Por ejemplo, ¿qué pasa si llamasMyEnum<Int> myIntEnum = MyEnum.<Int>value("some string property")
? No es posible implementar ese método para que funcione correctamente, por ejemplo, para lanzar una excepción o devolver un valor nulo cuando lo llama comoMyEnum.<Int>value("some double property")
debido a la eliminación de tipo.fuente
MyEnum<?>[] values()
yMyEnum<?> valueOf(...)
MyEnum<Int> blabla = valueOf("some double property");
porque los tipos no son compatibles. También, en ese caso, desea obtener un valor nulo porque desea devolver MyEnum <Int> que no existe para el nombre de propiedad doble y no puede hacer que ese método funcione correctamente debido a la eliminación.Enum
tiene muchas otras características útiles, y esta también sería opcional y muy útil ...Francamente, esto parece más una solución en busca de un problema que otra cosa.
El propósito completo de la enumeración de Java es modelar una enumeración de instancias de tipo que comparten propiedades similares de una manera que proporciona consistencia y riqueza más allá de las representaciones de cadenas o enteros comparables.
Tome un ejemplo de una enumeración de libros de texto. Esto no es muy útil o consistente:
¿Por qué querría que mis diferentes planetas tengan diferentes conversiones de tipo genérico? ¿Qué problema soluciona? ¿Justifica complicar la semántica del lenguaje? Si necesito este comportamiento, ¿es una enumeración la mejor herramienta para lograrlo?
Además, ¿cómo gestionaría conversiones complejas?
por ejemplo
Es bastante fácil
String
Integer
proporcionar el nombre u ordinal. Pero los genéricos le permitirían suministrar cualquier tipo. ¿Cómo gestionarías la conversiónMyComplexClass
? Ahora está compilando dos construcciones forzando al compilador a saber que hay un subconjunto limitado de tipos que se pueden suministrar a enumeraciones genéricas e introduciendo confusión adicional al concepto (genéricos) que ya parece eludir a muchos programadores.fuente
interface Converter<IN, OUT> { OUT convert(IN in); } <E> Set<E> convertListToSet(List<E> in, Converter<? super List<E>, ? extends Set<E>> converter) { return converter.convert(in); }
Tenemos que calcular manualmente cada vez qué tipo se consume y cuál se produce, y especificar los límites en consecuencia.Porque "enum" es la abreviatura de enumeración. Es solo un conjunto de constantes con nombre que se colocan en lugar de números ordinales para hacer que el código sea mejor legible.
No veo cuál podría ser el significado previsto de una constante parametrizada por tipo.
fuente
java.lang.String
:public static final Comparator<String> CASE_INSENSITIVE_ORDER
. ¿Ahora ves? :-)CASE_INSENSITIVE_ORDER
... SiComparator<T>
fuera una enumeración, ¿por qué no tener literales con ningún límite asociado<T>
?Creo que porque básicamente las enumeraciones no pueden ser instanciadas
¿Dónde establecerías la clase T si JVM te lo permitiera?
La enumeración es información que se supone que es siempre la misma, o al menos, que no cambiará dinámicamente.
nuevo MyEnum <> ()?
Aún así, el siguiente enfoque puede ser útil
fuente
setO()
método en unenum
. Considero constantes enumeraciones y para mí eso implica inmutable . Entonces, incluso si es posible, no lo haría.