¿Por qué los literales enum de Java no deberían tener parámetros generics?

Las enumeraciones de Java son geniales. Entonces son generics. Por supuesto, todos conocemos las limitaciones de este último debido al borrado de tipos. Pero hay una cosa que no entiendo, ¿Por qué no puedo crear una enumeración como esta?

public enum MyEnum { LITERAL1, LITERAL2, LITERAL3; } 

Este parámetro de tipo genérico a su vez podría ser útil en varios lugares. Imagine un parámetro de tipo genérico para un método:

 public  T getValue(MyEnum param); 

O incluso en la clase enum en sí:

 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

  • Enumeraciones, porque entonces puedo enumerar un conjunto finito de claves de propiedad
  • Genéricos, porque entonces puedo tener seguridad de tipo a nivel de método para almacenar propiedades
 public interface MyProperties { public  void put(MyEnum key, T value); public  T get(MyEnum key); } 

Ejemplo más concreto # 2

Tengo una enumeración de tipos de datos:

 public interface DataType {} public enum SQLDataType implements DataType { TINYINT, SMALLINT, INT, BIGINT, CLOB, VARCHAR, ... } 

Cada literal enum obviamente tendría propiedades adicionales basadas en el tipo genérico , mientras que al mismo tiempo, es una enumeración (inmutable, singleton, enumerable, etc., etc.)

Pregunta:

¿Nadie pensó en esto? ¿Es esto una limitación relacionada con el comstackdor? Teniendo en cuenta el hecho de que la palabra clave ” enum ” se implementa como azúcar sintáctica, que representa el código generado para la JVM, no entiendo esta limitación.

¿Quién puede explicarme esto? Antes de responder, considere esto:

  • Sé que los tipos generics se borran 🙂
  • Sé que hay soluciones con el uso de objetos de Clase. Son soluciones.
  • Los tipos generics dan como resultado conversiones de tipo generadas por el comstackdor cuando corresponda (por ejemplo, cuando se llama al método convert ()
  • El tipo genérico estaría en la enumeración. Por lo tanto, está obligado por cada uno de los literales de la enum. Por lo tanto, el comstackdor sabría qué tipo aplicar al escribir algo como String string = LITERAL1.convert(myObject); Integer integer = LITERAL2.convert(myObject); 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() . El comstackdor puede aplicar el tipo de conversión al llamar a String string = someClass.getValue(LITERAL1)

Esto ahora se está discutiendo a partir de las entradas optimizadas JEP-301 . El ejemplo dado en el JEP es, que es precisamente lo que estaba buscando:

 enum Argument { // declares generic enum STRING(String.class), INTEGER(Integer.class), ... ; Class clazz; Argument(Class clazz) { this.clazz = clazz; } Class getClazz() { return clazz; } } Class cs = Argument.STRING.getClazz(); //uses sharper typing of enum constant 

Desafortunadamente, el JEP todavía está luchando con problemas importantes: http://mail.openjdk.java.net/pipermail/amber-spec-experts/2017-May/000041.html

La respuesta está en la pregunta:

debido a la borradura de tipo

Ninguno de estos dos métodos es posible, ya que el tipo de argumento se borra.

 public  T getValue(MyEnum param); public T convert(Object); 

Para realizar esos métodos, puedes construir tu enumeración como:

 public enum MyEnum { LITERAL1(String.class), LITERAL2(Integer.class), LITERAL3(Object.class); private Class< ?> clazz; private MyEnum(Class< ?> clazz) { this.clazz = clazz; } ... } 

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 idioma bajo JEP 301: Enumeraciones mejoradas .

Hay otros métodos en ENUM que no funcionarían. ¿Qué MyEnum.values() ?

¿Qué pasa con MyEnum.valueOf(String name) ?

Por el valor de si crees que el comstackdor podría hacer un método genérico como

public static MyEnum valueOf (String name);

para llamarlo como MyEnum myStringEnum = MyEnum.value("some string property") , eso tampoco funcionaría. Por ejemplo, ¿qué MyEnum myIntEnum = MyEnum.value("some string property") si llama a MyEnum myIntEnum = MyEnum.value("some string property") ? No es posible implementar ese método para que funcione correctamente, por ejemplo, lanzar una excepción o devolver nulo cuando lo llame como MyEnum.value("some double property") debido a la MyEnum.value("some double property") del tipo.

Francamente, esto parece ser más una solución en la búsqueda de un problema que cualquier otra cosa.

El propósito completo de Java Enum es modelar una enumeración de instancias de tipo que comparten propiedades similares de una manera que proporcione consistencia y riqueza más allá de las representaciones de cadenas o números enteros comparables.

Tome un ejemplo de una enumeración de libro de texto. Esto no es muy útil o consistente:

 public enum Planet{ Earth, Venus, Mars ...etc. } 

¿Por qué querría que mis diferentes planetas tuvieran diferentes conversiones de tipo genérico? ¿Qué problema soluciona? ¿Justifica complicar la semántica del lenguaje? Si necesito este comportamiento, ¿es una enum la mejor herramienta para lograrlo?

Además, ¿cómo gestionaría las conversiones complejas?

por ejemplo

 public enum BadIdea{ INSTANCE1, INSTANCE2; } 

Es bastante fácil con String Integer para proporcionar el nombre o el ordinal. Pero los generics le permiten suministrar cualquier tipo. ¿Cómo administrarías la conversión a MyComplexClass ? Ahora estás eliminando dos construcciones al obligar al comstackdor a saber que hay un subconjunto limitado de tipos que se pueden suministrar a las enumeraciones genéricas e introducir una confusión adicional al concepto (Generics) que ya parece eludir a muchos progtwigdores.

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.

Creo que porque básicamente los Enums no pueden ser instanciados

¿Dónde establecerías la clase T, si JVM te lo permitiera?

La enumeración es información que se supone que siempre es la misma, o al menos, que no cambiará dinámicamente.

nuevo MyEnum <> ()?

Aún el siguiente enfoque puede ser útil

 public enum MyEnum{ LITERAL1("s"), LITERAL2("a"), LITERAL3(2); private Object o; private MyEnum(Object o) { this.o = o; } public Object getO() { return o; } public void setO(Object o) { this.o = o; } }