Pourquoi les littéraux Java enum ne devraient-ils pas avoir des parameters de type génériques?

Les énumérations de Java sont excellentes. Les génériques aussi. Bien sûr, nous connaissons tous les limites de ce dernier en raison de l’effacement de type. Mais il y a une chose que je ne comprends pas, pourquoi je ne peux pas créer un enum comme ceci:

public enum MyEnum { LITERAL1, LITERAL2, LITERAL3; } 

Ce paramètre de type générique à son tour pourrait alors être utile à divers endroits. Imaginez un paramètre de type générique à une méthode:

 public  T getValue(MyEnum param); 

Ou même dans la classe enum elle-même:

 public T convert(Object o); 

Exemple plus concret # 1

Étant donné que l’exemple ci-dessus peut sembler trop abstrait pour certains, voici un exemple plus concret de la raison pour laquelle je veux le faire. Dans cet exemple, je veux utiliser

  • Enums, car alors je peux énumérer un ensemble fini de clés de propriété
  • Génériques, car alors je peux avoir une sécurité de type au niveau de la méthode pour stocker les propriétés
 public interface MyProperties { public  void put(MyEnum key, T value); public  T get(MyEnum key); } 

Exemple plus concret # 2

J’ai une énumération des types de données:

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

Chaque enum littéral aurait évidemment des propriétés supplémentaires basées sur le type générique , tout en étant enum (immuable, singleton, enumerable, etc. etc.)

Question:

Est-ce que personne n’y a pensé? Est-ce une limitation liée au compilateur? Compte tenu du fait que le mot-clé ” enum ” est implémenté en tant que sucre syntaxique, représentant le code généré pour la machine virtuelle Java, je ne comprends pas cette limitation.

Qui peut m’expliquer cela? Avant de répondre, considérez ceci:

  • Je sais que les types génériques sont effacés 🙂
  • Je sais qu’il existe des solutions de contournement en utilisant des objects de classe. Ce sont des solutions de contournement.
  • Les types génériques génèrent des transtypages générés par le compilateur partout où ils sont applicables (par exemple, lors de l’appel de la méthode convert ())
  • Le type générique serait sur la liste. Par conséquent, il est lié par chacun des littéraux de la énumération. Par conséquent, le compilateur devrait savoir quel type appliquer lors de l’écriture de quelque chose comme Ssortingng ssortingng = LITERAL1.convert(myObject); Integer integer = LITERAL2.convert(myObject); Ssortingng ssortingng = LITERAL1.convert(myObject); Integer integer = LITERAL2.convert(myObject);
  • La même chose s’applique au paramètre de type générique dans la T getvalue() . Le compilateur peut appliquer un transtypage lors de l’appel de Ssortingng ssortingng = someClass.getValue(LITERAL1)

Ceci est en cours de discussion à partir de JEP-301 Enhanced Enums . L’exemple donné dans le JEP est précisément ce que je cherchais:

 enum Argument { // declares generic enum STRING(Ssortingng.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 

Malheureusement, le JEP est toujours aux sockets avec des problèmes importants: http://mail.openjdk.java.net/pipermail/amber-spec-experts/2017-May/000041.html

La réponse est dans la question:

à cause de l’effacement de type

Aucune de ces deux méthodes n’est possible, car le type d’argument est effacé.

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

Pour réaliser ces méthodes, vous pouvez cependant construire votre enum comme:

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

Parce que tu ne peux pas. Sérieusement. Cela pourrait être ajouté à la spécification de langue. Ça n’a pas été Cela appendait une certaine complexité. Cet avantage par rapport au coût signifie qu’il n’est pas une priorité.

Mise à jour: actuellement ajouté à la langue sous JEP 301: Enums améliorés .

Il y a d’autres méthodes dans ENUM qui ne fonctionneraient pas. Que MyEnum.values() ?

Qu’en est-il de MyEnum.valueOf(Ssortingng name) ?

Pour le valueOf si vous pensez que le compilateur pourrait faire une méthode générique comme

public static MyEnum valueOf (nom de chaîne);

pour l’appeler comme MyEnum mySsortingngEnum = MyEnum.value("some ssortingng property") , cela ne fonctionnerait pas non plus. Par exemple, si vous appelez MyEnum myIntEnum = MyEnum.value("some ssortingng property") ? Il n’est pas possible d’implémenter cette méthode pour fonctionner correctement, par exemple pour lancer une exception ou retourner une valeur nulle lorsque vous l’appelez comme MyEnum.value("some double property") raison de l’effacement de type.

Franchement, cela semble être plus une solution à la recherche d’un problème qu’autre chose.

Le but de java enum est de modéliser une énumération d’instances de type qui partagent des propriétés similaires de manière à assurer la cohérence et la richesse au-delà des représentations de chaînes ou d’entiers comparables.

Prenons un exemple de recueil de livres. Ce n’est pas très utile ou cohérent:

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

Pourquoi voudrais-je que mes différentes planètes aient des conversions de types génériques différentes? Quel problème résout-il? Est-ce que cela justifie de compliquer la sémantique du langage? Si j’en ai besoin, ce comportement est-il le meilleur outil pour y parvenir?

De plus, comment gérez-vous les conversions complexes?

par exemple

 public enum BadIdea{ INSTANCE1, INSTANCE2; } 

Il est assez facile avec Ssortingng Integer pour fournir le nom ou ordinal. Mais les génériques vous permettraient de fournir n’importe quel type. Comment gérez-vous la conversion vers MyComplexClass ? Maintenant, en construisant deux constructions en forçant le compilateur à savoir qu’il existe un sous-ensemble limité de types pouvant être fournis aux énumérations génériques et en introduisant une confusion supplémentaire au concept (Generics) qui semble déjà échapper à beaucoup de programmeurs.

Becasue “enum” est l’abréviation de énumération. C’est juste un ensemble de constantes nommées qui remplacent les nombres ordinaux pour rendre le code plus lisible.

Je ne vois pas quelle pourrait être la signification d’une constante paramétrée.

Je pense que fondamentalement, Enums ne peut pas être instancié

Où définiriez-vous la classe T, si JVM vous permettait de le faire?

L’énumération est une donnée qui est censée être toujours la même, ou du moins, elle ne changera pas de façon dinamérique.

nouveau MyEnum <> ()?

L’approche suivante peut néanmoins être utile

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