Tu pourrais détailler cela ???
a++
Version imprimable
Deux enums, bien évidemment différents.
Une fonction (de classe) générique qui va attacher chaque enum à sa combobox (GXT) (entre autres multiples choses), et qui va aussi vouloir avoir accès à la fonction values() générée à la volée par le compilo (?) et qui n'appartient bien évidement pas à la classe Enum<> (ce qui se comprend pour une fonction static finale).
Finalement, je m'en suis sorti en passant le résultat de la fonction values() à ma fonction au lieu de laisser la fonction obtenir toute seule ce dont elle avait besoin des enums.
Quelque chose comme cela ?
Mais je t'accorde qu'une méthode static Enum.values(Class<Enum>) aurait été plus pratique ;)Code:
1
2
3
4
5
6
7 public static <T extends Enum<T>> void method(Enum<T> uneValeur) { T[] values = uneValeur.getDeclaringClass().getEnumConstants(); System.out.println("uneValeur : " + uneValeur); System.out.println("values : " + Arrays.toString(values) ); }
a++
C'est à peine différent de ce que j'ai fait. L'idéal étant de ne passer aucune valeur à la fonction, et de se contenter de spécifier le type de l'énuméré à la classe (dans mon cas) générique.
Ah ok je comprend ce que tu veux dire. Le problème vient de la perte du typeage des Generics. Tu peux t'en sortir en utilisant la classe en paramètre :
Code:
1
2
3
4
5
6
7
8
9
10 public class MaClass<T extends Enum<T>> { private final Class<T> enumType; public MaClass(Class<T> enumType) { this.enumType = enumType; } public T[] values() { return this.enumType.getEnumConstants(); }
Mais le problème vient surtout de l'approche radicalement différente entre les templates et les Generics ;)
a++
PS : Au passage par curiosité : il est possible en C++ d'obtenir toutes les valeurs d'une enum ??? Car cela ne me semble pas faisable...
Moyennant feinte, cela se fait très bien -- après la situation sera un peu plus rose en C++0x si mes souvenirs sont bons.
Voilà une façon de faire, parmi d'autres possibles.Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 struct MyEnum { enum type { FIRST, toto = FIRST, titi, tutu, LAST }; static char const* toString(type i) { char const* k_values[] = {"To to", "Ti ti", "Tu tu"}; STATIC_ASSERT(array_size(k_values)==LAST, Sizes_mismatch); assert(i>=FIRST && i <LAST && "out of range"); return k_values[i] } static type next(type e) { return type(e+1); } }; template <typename MyEnum> printAll() { for (MyEnum::type e=MyEnum::FIRST ; e!=MyEnum::LAST ; e=MyEnum::next(e)) cout << MyEnum::toString(e) << "\n"; } ... // l'appel qui ne me fait pas me répéter 3 fois: printAll<MyEnum>();
La production de l'énum demande un peu d'huile de coude qui se résout très bien avec un fichier squelette ou une phase de préprocessing.
Ce qui revient exactement à ce qui avait été dit : chacun ses habitudes et ses réflexes qu'il faut impérativement adapter.
Sinon, pour le code Java, Là j'ai l'impression que j'économise juste le passage du values() en paramètre pour quoi d'autre ? un .class ?
?Code:MyFantasticToolBar<MyEnum1> tb = new MyFantasticToolBar<MyEnum1>(MyEnum1.class)
(Déjà que je souffre à taper les " = new LeTypeQueJeCopieColle" (j'ai un peu de mal avec eclipse sous macbookpro))
Mais ... est-on obligés de passer le enumType en paramètre ? Ne peut-on pas se contenter d'un
histoire de simplifier le code appelant ?Code:
1
2
3
4
5
6 public class MaClass<T extends Enum<T>> { public MaClass() { final Class<T> enumType = new Class<T>(); final T[] values = enumType.getEnumConstants(); } }
Non car le type T ne peut pas être connu à l'exécution : les Generics n'effectue pas de duplication de code et ne sont pas fortement typé à l'exécution. Du coup impossible de faire des new T() ou autre sans se trainer le type Class correspondant.
Par contre cela a d'autres avantages comme la covariance. Avec ceci :
On peut afficher tous les éléments de n'importe quel enum tout simplement :Code:
1
2
3
4
5 public static <T extends Enum<T>> void printAll(Class<T> enumType) { for (T value : enumType.getEnumConstants()) { System.out.println(value); } }
a++Code:printAll(MyEnum.class);
- Le constructeur de Class n'est pas visible. Donc on ne peut pas comme ça.
- On ne peut pas faire non plus T.class
- On ne peut pas faire non plus new T().getClass() car malheureusement et contrairement à C++ (je crois), on n'a pas le droit au new T();
Bertrand Meyer explique à la page 318 de CPOO pourquoi la création d'un générique de cette manière est illégale car on ne sait rien des procédures de création du type T. Perso, j'aurai voulu que la jre essaye avec le constructeur vide quitte à ce qu'elle me balance une exception.
Bref, si vous avez d'autres idées ...
Suite au manque que je relève dans mon message précédent, j'ai fait quelques essais...
J'avais dans l'idée de faire un truc similaire à String.class.newInstance() ) mais je ne peux pas faire T.class.newInstance() pour feinter leur interdiction de faire un new T() :mrgreen:
Je n'ai pas regardé le fonctionnement interne des generics en Java alors je joue au naif ...
Si c'est un remplacement à la compilation, pourquoi il remplace pas mon T.class par String.class si mon T vaut String ? Il le fait bien pour le retour des méthodes ? non ?
On va me répondre que tout ce décide à l'exécution ...
Mais alors, pourquoi je peux faire :
avecCode:
1
2
3 List l = createInstance(ArrayList.class); l.add("i"); System.out.println(l);
Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 @SuppressWarnings("unchecked") protected <T extends Object> T createInstance(Class<T> t) { try { return (T) Class.forName(t.getName()).newInstance(); } catch (Exception e) { // Je conçois bien que pour certains types, le constructeur // ne soit pas visible mais je l'assume e.printStackTrace(); return null; } }
Bon, j'admets, c'est un peu violent :aie: :mrgreen:
L'introspection, c'est bien a l'exécution et il le connait bien mon type !
Je suis perplexe ... Qu'est ce que j'ai loupé ?
Personne n'a cité la const correctness du c++ sur ce thread?
Non : contrairement aux Templates du C++n les Generics n'effectuent pas pas de remplacement à la compilation. Les Generics effectuent une vérification de la cohérence du typage à la compilation afin de t'assurer un code sécurisé (pas de ClassCastException), mais réutilisent à l'exécution le même code !
Tu est obligé d'utiliser un paramètre de type Class pour pouvoir faire cela, et de passer par la reflection. Dans ce cas à l'exécution tu peux connaitre le type du paramétrage.
D'ailleurs tu pourrais écrire directement ceci :
:arrow: Il n'y a plus besoin de SuppressWarning car le typeage est sécurisé par les Generics ;)Code:
1
2
3
4
5
6
7 protected <T extends Object> T createInstance(Class<T> t) { try { return t.newInstance(); } catch (Exception e) { return null; } }
:arrow: Les Generics ne sont pas des Templates comme les autres !
a++
Merci adiGuba pour ces éclaircissements.
En effet, je me suis rendu compte par la suite que je passais la classe, donc dans une class templaté qui voudrait créer un objet du type templaté, on devrait avoir quand même :
new MaClass<ArrayList>(ArrayList.class);
Mon dieu que c'est laid !
C'est un préjugé. J'ai vu des développeurs C++ débuter la programmation 3D en se servant d'un moteur 3D complet au lieu de passer par une API de bas niveau. N'étant pas statisticien, je ne m'avancerai pas sur leur nombre.
De plus, à ma connaissance, il n'y a pas de cursus scolaire (en France) où on ne fait que du Java sans avoir un minimum de base en algorithmique, je pense notamment aux cursus universitaires au sens large (licence générale d'informatique, DUT).
Je suis d'accord, je pense notamment à un benchmark OpenGL écrit en Java avec un Thread.sleep(long millis) qui n'avait rien à faire là alors que ça aurait eu un sens en C/C++ dans ce cas de figure.
La présence des pointeurs avec une grande liberté dans leur arithmétique est un obstacle à certaines optimisations de gestion de la mémoire au niveau du compilateur en C++, obstacle levé de fait en Java selon Brian Goetz.
Ce n'est pas un préjugé, je n'ai pas cherché a généraliser (loin de moi cette idée), j'ai simplement parlé de connaissances personnelles qui ont tendance à empiler des framework pour faire des trucs relativement simple à la base uniquement parce que c'est tendance. heureusement pour moi d'autres sont plus pragmatiques et ont tendance à tempérer ces derniers.
Ensuite personnellement je ne me lancerai pas dans de la programmation 3D sans avoir un minimum étudié les concepts qui sont derrières et ce quelque soit le langage que je devrais utiliser derrière.
heureusement qu'il ne font pas que du java, sinon j'aurais peur.Citation:
De plus, à ma connaissance, il n'y a pas de cursus scolaire (en France) où on ne fait que du Java sans avoir un minimum de base en algorithmique, je pense notamment aux cursus universitaires au sens large (licence générale d'informatique, DUT).
Cependant certains façon de faire en algorithme passe mieux dans certains langage que d'autres.
Pour bosser sur les deux :
- Java rajoute 20% de productivité (environ). La lib Java permet de toujours retrouver ses petits quand on débarque sur un nouveau projet.
- C++ rajoute un paquet de performances. C++ est plus propre, parce que plus violent (Java encourage un peu trop facilement les erreurs silencieuses, quand C++ crashe directement).
Porter du code C++ est largement possible (et pas fondamentalement dur). Perso, ça m'a pris une semaine pour porter une appli full windows/VC++ vers du Linux/GCC, essentiellement parce que je ne savais pas comment faire quand j'ai commencé. Mieux vaut juste le faire avant que la taille du projet rende la chose atroce.
Pour moi, la plus grosse différence entre les deux langages (au delà des questions de lib, faire de la 3D en Java étant plutôt ardu) est le garbage collector. Sur une appli temps réel, c'est en général ingérable d'avoir des blocages imprévisibles pouvant monter à la seconde. Sur une appli sans trop de grosses contraintes à l'exécution, alors, je choisirai Java sans me poser de questions.
J'ai souvent vu ça :
Le gros avantage étant que ça marche, va juste y avoir un traitement d'ignoré, basiquement, un message client->serveur non traité, ou un click souris d'annulé, mais ça ne mets pas toujours en danger la stabilité du soft.Code:
1
2
3
4
5
6
7
8
9 while (true) { try { // Ma boucle principale } catch (Exception e) log e; }
Par contre... ça encourage à ne pas corriger l'erreur si celle ci ne génère aucun effet visible et que son taux de reproduction est minime. Le résultat étant que toutes les prochaines erreurs deviennent silencieuses, en ce sens qu'elles génèrent le même log. Et y a un moment ou le soft devient ingérable.
En C++, pas de try catch (au delà de ceux que tu génères toi même) et donc, pas de récupération d'erreur comme celle ci.
Après, ça peut passer pour un défaut de conception, même si dans certains cas je le pronerai (sur un serveur, c'est même un raccourci génial, tu sais que si un traitement plante bizarrement, ton serveur continue de tourner). Mais il faut derrière absolument que les dévs corrigent l'intégralité des bugs générés, et ça, c'est pas toujours donné.
Hein? :aie:
Il y a surtout un moment où le développeur doit se remettre en question. Dans ma boîte, le type qui écrit un catch(Exception) sans une très bonne raison est assuré de passer par la case goudron et plumes.
Tu peux pas mettre des lacunes aussi évidentes de gestion d'erreur sur le dos d'un langage. Dans ce cas que tu montres, ce n'est même pas un truc subtil qu'on pourrait faire sans faire gaffe, mais une faute de conception grossière.
Depuis quand le but d'une gestion d'exception est de laisser continuer l'application coûte que coûte dans un état indéfini et potentiellement instable? Le but d'une exception n'est pas de jeter sous le tapis des erreurs potentiellement graves pour donner un faux sentiment de stabilité.
Franchement je peux pas être d'accord avec ton point de vue. Et je maintiens que catcher une exception au hasard sans être sûr de pouvoir la gérer tient du vice.
En fait je vais te le dire, ne le prends pas mal mais à mon avis, comme beaucoup de développeur, tu ignores comment les exceptions doivent s'utiliser.
Oui en java il y a 2 types d'exceptions : les checked exceptions et les unchecked exceptions.
Les checked on est obligé de les catcher et de prévoir un traitement, les unchecked sont sensé ne pas arriver et donc il n'est pas obligatoire de prévoir un traitement.
Si une exception n'est pas catchée par l'appelant de la méthode qui l'envoie, il faut signaler qu'elle sera envoyée au dessus. Ainsi on est sur qu'elle sera traitée à un moment ou à un autre (choisi par le développeur).
Ce genre de boucle peut paraître idiote, mais si on ne sait pas comment est le reste du programme on ne peut rien en déduire.
Si c'est un serveur, l'important est qu'en toute circonstance ce dernier fonctionne et ne plante pas totalement au premier imprévu.
Après dans les logs de ton catch tu traces suffisamment pour être capable de savoir ce qui s'est passé et corriger le bug.
Sachant qu'en principe ce genre de catch ne doit attraper que les unchecked exceptions (NullPointerException par exemple que l'on ne doit jamais catcher car si elles arrivent il s'agit d'un bug), OutOfMemoryError en cas de dépassement mémoire, ce qui peut arriver sans que ce soit un bug).
En tout cas il vaut mieux que le programme retombe sur ses pieds si possible plutôt que le planter totalement.