Les énumérations 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 ne puis-je pas créer une énumération comme celle-ci:
public enum MyEnum<T> {
LITERAL1<String>,
LITERAL2<Integer>,
LITERAL3<Object>;
}
Ce paramètre de type générique peut <T>
à son tour être utile à divers endroits. Imaginez un paramètre de type générique pour une méthode:
public <T> T getValue(MyEnum<T> param);
Ou même dans la classe enum elle-même:
public T convert(Object o);
Exemple plus concret n ° 1
Étant donné que l'exemple ci-dessus peut sembler trop abstrait pour certains, voici un exemple plus réel de la raison pour laquelle je veux faire cela. Dans cet exemple, je souhaite utiliser
- Enums, car je peux alors é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 <T> void put(MyEnum<T> key, T value);
public <T> T get(MyEnum<T> key);
}
Exemple plus concret n ° 2
J'ai une énumération de types de données:
public interface DataType<T> {}
public enum SQLDataType<T> implements DataType<T> {
TINYINT<Byte>,
SMALLINT<Short>,
INT<Integer>,
BIGINT<Long>,
CLOB<String>,
VARCHAR<String>,
...
}
Chaque littéral d'énumération aurait évidemment des propriétés supplémentaires basées sur le type générique <T>
, tout en étant en même temps une énumération (immuable, singleton, énumérable, etc.)
Question:
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 du code généré vers la JVM, 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 utilisant des objets de classe. Ce sont des solutions de contournement.
- Les types génériques entraînent des casts de type générés par le compilateur le cas échéant (par exemple lors de l'appel de la méthode convert ()
- Le type générique <T> serait sur l'énumération. Par conséquent, il est lié par chacun des littéraux de l'énumération. Par conséquent, le compilateur saurait quel type appliquer lors de l'écriture de quelque chose comme
String string = 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()
méthode. Le compilateur peut appliquer la conversion de type lors de l'appelString string = someClass.getValue(LITERAL1)
enum
en un idiome "typesafe enum" que nous utilisions avant Java 1.5. Soudainement, vous pouvez paramétrer vos membres enum. C'est probablement ce que je vais faire maintenant.Réponses:
Ceci est maintenant discuté à partir des Enums améliorés JEP-301 . L'exemple donné dans le JEP est, et c'est précisément ce que je recherchais:
Malheureusement, le JEP est toujours aux prises avec des problèmes importants: http://mail.openjdk.java.net/pipermail/amber-spec-experts/2017-May/000041.html
la source
La réponse est dans la question:
Aucune de ces deux méthodes n'est possible, car le type d'argument est effacé.
Pour réaliser ces méthodes, vous pouvez cependant construire votre énumération comme suit:
la source
public T convert(Object);
. J'imagine que cette méthode pourrait par exemple restreindre un tas de types différents à <T>, qui par exemple est une chaîne. La construction de l'objet String est à l'exécution - rien que le compilateur ne fait. Par conséquent, vous devez connaître le type d'exécution, tel que String.class. Ou est-ce que je manque quelque chose?public final static String FOO;
avec un bloc statiquestatic { FOO = "bar"; }
- les constantes sont également évaluées même si elles sont connues au moment de la compilation.Parce que tu ne peux pas. Sérieusement. Cela pourrait être ajouté à la spécification de la langue. Cela n'a pas été le cas. Cela ajouterait une certaine complexité. Cet avantage par rapport aux coûts signifie que ce n'est pas une priorité élevée.
Mise à jour: en cours d'ajout à la langue sous JEP 301: Enums améliorés .
la source
Il existe d'autres méthodes dans ENUM qui ne fonctionneraient pas. Qu'est-ce qui
MyEnum.values()
reviendrait?Et quoi
MyEnum.valueOf(String name)
?Pour valueOf si vous pensez que ce compilateur pourrait créer une méthode générique comme
pour l'appeler comme ça
MyEnum<String> myStringEnum = MyEnum.value("some string property")
, ça ne marcherait pas non plus. Par exemple, que faire si vous appelezMyEnum<Int> myIntEnum = MyEnum.<Int>value("some string property")
? Il n'est pas possible d'implémenter cette méthode pour qu'elle fonctionne correctement, par exemple pour lever une exception ou renvoyer null lorsque vous l'appelez comme enMyEnum.<Int>value("some double property")
raison de l'effacement de type.la source
MyEnum<?>[] values()
etMyEnum<?> valueOf(...)
MyEnum<Int> blabla = valueOf("some double property");
car les types ne sont pas compatibles. Vous souhaitez également dans ce cas obtenir null car vous souhaitez renvoyer MyEnum <Int> qui n'existe pas pour le nom de propriété double et vous ne pouvez pas faire fonctionner cette méthode correctement en raison de l'effacement.Enum
a beaucoup d'autres fonctionnalités utiles, et celle-ci serait facultative et très utile aussi ...Franchement, cela semble être plus une solution à la recherche d'un problème qu'autre chose.
Le but entier de l'énumération java est de modéliser une énumération d'instances de type qui partagent des propriétés similaires d'une manière qui offre une cohérence et une richesse au-delà de celles des représentations String ou Integer comparables.
Prenons un exemple d'énumération de manuel. Ce n'est pas très utile ou cohérent:
Pourquoi voudrais-je que mes différentes planètes aient différentes conversions de types génériques? Quel problème résout-il? Cela justifie-t-il de compliquer la sémantique du langage? Si j'ai besoin de ce comportement, est-ce qu'une énumération est le meilleur outil pour y parvenir?
De plus, comment géreriez-vous des conversions complexes?
par exemple
Il est assez facile
String
Integer
de fournir le nom ou l'ordinal. Mais les génériques vous permettraient de fournir n'importe quel type. Comment géreriez-vous la conversion versMyComplexClass
? Maintenant, vous détruisez deux constructions en forçant le compilateur à savoir qu'il existe un sous-ensemble limité de types qui peuvent être fournis aux énumérations génériques et en introduisant une confusion supplémentaire dans le concept (Generics) qui semble déjà échapper à beaucoup de programmeurs.la source
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); }
Nous devons déterminer manuellement à chaque fois quel type est consommé et lequel produit, et spécifier les limites en conséquence.Becasue "enum" est l'abréviation d'é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 voulue d'une constante paramétrée par type.
la source
java.lang.String
:public static final Comparator<String> CASE_INSENSITIVE_ORDER
. Tu vois maintenant? :-)CASE_INSENSITIVE_ORDER
... SiComparator<T>
c'était une énumération, pourquoi ne pas avoir des littéraux avec une liaison associée<T>
?Je pense parce 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 des données qui sont censées être toujours les mêmes, ou du moins, qui ne changeront pas de façon dynamique.
nouveau MyEnum <> ()?
L'approche suivante peut néanmoins être utile
la source
setO()
méthode sur un fichierenum
. Je considère les enums constantes et pour moi cela implique immuable . Donc même si c'est possible, je ne le ferais pas.