CSC4102 : Qualit e du code JAVA et introduction aux idiomes JAVA - Denis Conan Revision : 3800
←
→
Transcription du contenu de la page
Si votre navigateur ne rend pas la page correctement, lisez s'il vous plaît le contenu de la page ci-dessous
CSC4102 : Qualité du code JAVA et introduction aux idiomes JAVA Revision : 3800 Denis Conan Janvier 2019
Sommaire 1. Motivations et objectifs 2. Outils d’analyse statique de code = Pour aider ! 3. Définition idiome/patron d’implémentation 4. Idiomes JAVA, premiers exemples, méthodes de la classe Object communes à tous les objets 5. Mise en pratique en TP (2h) + HP (3h) 2/32 01/2019 Denis Conan CSC4102 : Qualité du code JAVA et introduction aux idiomes JAVA
1 Motivations et objectifs 1.1 Qui demande à améliorer la qualité du code ? 1.2 Pourquoi les idiomes ? 1.3 Quels sont les objectifs de la séance ? 3/32 01/2019 Denis Conan CSC4102 : Qualité du code JAVA et introduction aux idiomes JAVA
1.1 Qui demande à améliorer la qualité du code ? « The Google FindBugs Fixit » [Ayewah and Pugh, 2010] In May 2009, Google conducted a company wide FindBugs/SpotBugs “fixit” 1 . Hundreds of engineers reviewed thousands of FindBugs/SpotBugs warnings. Static analysis tools [such as FindBugs/SpotBugs ] scan software looking for issues that might cause defective behavior. Most interesting software quality problems were eventually found and fixed without FindBugs/SpotBugs, but FindBugs/SpotBugs could have found these problems early, when they are cheap to remediate. • These observations [are attributed] to the success of Google’s testing and monitoring practices. Engineers viewed FindBugs warnings as worth fixing. • Over 77% of the reviews identified the warnings as real defects and recommended fixing the underlying problems. 1. C’est ce que l’on appelle une revue de code, ici avec FindBugs/SpotBugs [Ayewah et al., 2008] 4/32 01/2019 Denis Conan CSC4102 : Qualité du code JAVA et introduction aux idiomes JAVA
1.2 Pourquoi les idiomes ? G.L. Steele, ACM Grace Murray Hopper Award, 1988 Foreword of « Effective JAVA » [Bloch, 2008] If you ever studied a second language yourself and then tried to use it outside the classroom, you know that there are three things you must master : 1. How the language is structured (grammar) 2. How to manage things you want to talk about (vocabulary) 3. The customary and effective ways to say everyday things (usage) It is much the same with a programming language. 1. You need to understand the core language [JAVA = object-orientation] 2. You need to know the vocabulary [e.g. the standard library for collections] 3. You need to be familiar with the customary and effective ways to structure your code − C’est ce qui s’appelle les patrons d’implémentation ou idiomes 5/32 01/2019 Denis Conan CSC4102 : Qualité du code JAVA et introduction aux idiomes JAVA
1.3 Quels sont les objectifs de la séance ? Étudier la qualité du code produit dans le Sprint 1 • On paie de la dette technique • Avec les outils FindBugs/SpotBugs et CheckStyle − On atténue les efforts nécessaires à l’aide d’outil d’analyse statique du code Étudier l’application d’idiomes JAVA • On améliore la qualité du code en améliorant nos connaissances sur le langage • À titre d’exemple, sur les méthodes communes à toutes les classes : equals, hashCode, toString, etc. Continuer le développement de l’application de l’étude de cas • On annonce dès maintenant les nouveaux cas d’utilisation pour l’ensemble du Sprint 2 6/32 01/2019 Denis Conan CSC4102 : Qualité du code JAVA et introduction aux idiomes JAVA
2 Outils d’analyse statique de code = Pour aider ! 2.1 Analyse statique de code 2.2 FindBugs/SpotBugs 2.3 CheckStyle 7/32 01/2019 Denis Conan CSC4102 : Qualité du code JAVA et introduction aux idiomes JAVA
2.1 Analyse statique de code 1. Image extraite du cours CSC4536 : http://www-inf.int-evry.fr/COURS/CSC4536/web/ 8/32 01/2019 Denis Conan CSC4102 : Qualité du code JAVA et introduction aux idiomes JAVA
2.2 FindBugs/SpotBugs Catégories (http://findbugs.sourceforge.net/demo.html) • Correctness bug : Probable bug, an apparent coding mistake resulting in code that was probably not what the developer intended − We strive for a low false positive rate • Bad Practice : Violations of recommended and essential coding practice − Examples include hash code and equals problems, cloneable idiom, dropped exceptions, serializable problems, and misuse of finalize − We strive to make this analysis accurate, although some groups may not care about some of the bad practices • Dodgy (douteux, risqué) : Code that is confusing, anomalous, or written in a way that leads itself to errors − Examples include dead local stores, switch fall through, unconfirmed casts, and redundant null check of value known to be null − More false positives accepted 1. FindBugs/SpotBugs doesn’t try to identify all defects in a particular category [Ayewah et al., 2008] 9/32 01/2019 Denis Conan CSC4102 : Qualité du code JAVA et introduction aux idiomes JAVA
2.2.1 Exemples, a/f 1 X x2 = new X2 (...) ; 2 h2 . put ( x2 , " c ’ est ␣ un ␣ objet " ) ; « X2 doesn’t define a hashCode() method but is used in a hashed data structure in main(String[]) » • « A class defines an equals(Object) method but not a hashCode() method, and thus doesn’t fulfill the requirement that equal objects have equal hashCodes. An instance of this class is used in a hash data structure, making the need to fix this problem of highest importance. » − Le problème vient de ce que la classe possède une méthode equals mais pas de méthode hashCode 10/32 01/2019 Denis Conan CSC4102 : Qualité du code JAVA et introduction aux idiomes JAVA
2.2.2 Exemples, b/f 1 pi = null ; 2 for ( it = vPersonnes . iterator () ; it . hasNext () ; pi = it . next () ) { 3 if ( pi . equals ( new Personne ( " Dupont " +5 , " Jules " ," 19102271271 " +5) ) ) { 4 it . remove () ; 5 } 6 } « Déréférencement d’un pointeur null dans la méthode main(String[]) » • « Un pointeur à null est déréférencé ici. Ceci va mener à une NullPointerException quand le code sera exécuté. » − Le problème est à la ligne 2 : l’affectation « pi = it.next() » n’a lieu qu’en fin de boucle ; donc, lors de l’entrée dans la boucle for, pi vaut sa valeur avant la boucle, c’est-à-dire ici null 11/32 01/2019 Denis Conan CSC4102 : Qualité du code JAVA et introduction aux idiomes JAVA
2.2.3 Exemples, c/f 1 if ( p . getPrenom () == p1 . getPrenom () ) { // ... « Comparaison d’objets String utilisant == ou !=. » • « Ce code compare des objets String au moyen de l’égalité par référence des opérateurs == ou !=. À moins que les deux chaı̂nes ne soient des constantes dans le fichier source ou aient été internalisées au moyen de la méthode String.intern(), deux chaı̂nes identiques peuvent être représentées par deux objets String différents. Envisagez d’utiliser la méthode equals(Object) à la place. » − FindBugs/SpotBugs signale l’utilisation de l’opérateur « == » à la place de la méthode « equals ». C’est sans aucun doute une erreur pour des variables qui sont des références sur des objets. 12/32 01/2019 Denis Conan CSC4102 : Qualité du code JAVA et introduction aux idiomes JAVA
2.2.4 Exemples, d/f 1 Calendar o2 = o1 ; 2 System . out . println ( " o1 ␣ == ␣ o2 ␣ : ␣ " + ( o1 == o2 ) + " ,\ t ␣ o1 . equals ( o2 ) ␣ : ␣ " + ( o1 . equals ( o2 ) ) ) ; This method compares a local variable with itself, and may indicate a typo or a logic error. Make sure that you are comparing the right things. • L’alerte vient de la combinaison de l’affectation de la ligne 1 avec l’appel de la méthode equals à la ligne 2 13/32 01/2019 Denis Conan CSC4102 : Qualité du code JAVA et introduction aux idiomes JAVA
2.2.5 Exemples, e/f 1 public Audio ( final String code , final Localisation localisation , 2 final String titre , final String auteur , final String annee , 3 final Genre genre , final String classif ) { 4 super ( code , localisation , titre , auteur , annee , genre ) ; 5 if ( classif == null ) { 6 throw new I l l e g a l A r g u m e n t E x c e p t i o n ( " Ctr ␣ Audio ␣ classification ␣ = ␣ " + cl assif icati on ) ; } 7 this . classi ficat ion = classif ; 8 } « Lecture du champ Audio.classification non initialisé dans new Audio(...). » • « Ce constructeur lit un champ qui n’a pas encore été initialisé. Une des causes les plus fréquentes est l’utilisation accidentelle par le développeur du champ au lieu d’un des paramètres du constructeur. » − Le problème est à la ligne 6 : on voulait sans aucun doute utiliser l’argument classif au lieu de l’attribut classification • Mais, dans ce cas, on sait que c’est null qui sera affiché 14/32 01/2019 Denis Conan CSC4102 : Qualité du code JAVA et introduction aux idiomes JAVA
2.2.6 Exemples, f/f Étant donné les classes suivantes : 1 public class Document { // ... 2 public class Audio extends Document { // ... Problème dans le code qui suit : 1 Document seigneur = new Document ( " C007 " , " Le ␣ seigneur ␣ des ␣ anneaux " , 2 " Tolkien " , " 1950 " ) ; 3 System . out . println ( " seigneur ␣ est ␣ un ␣ Audio ␣ : ␣ " + ( seigneur i n s t a n c e o f Audio ) ) ; « instanceof renvoyant toujours faux dans main(String[]), puisque qu’aucun Audio ne peut être un seigneur. » • « Ce test instanceof renverra toujours faux. Même s’il est sûr, assurez-vous qu’il ne s’agisse pas d’une erreur logique ou d’une mauvaise compréhension. » − Le problème est à la ligne 3 avec un test instanceof qui n’est pas pertinent 15/32 01/2019 Denis Conan CSC4102 : Qualité du code JAVA et introduction aux idiomes JAVA
2.3 CheckStyle Comme son nom le suggère, adhésion à un standard de codage • Deux fournis par défaut dans Eclipse − Google checks et Sun Checks Pour nous, l’objectif numéro 1 est la documentation Javadoc • Configuration particulière de CheckStyle fournie en TP − Feuille de style construite à partir de Sun Checks • Nous autorisons les tabulations, les caractères « » dans les noms de paquettages, les lignes à plus de 80 colonnes, etc. • Nous ne développons pas une bibliothèque • Uniquement pour une classe • Génération de la documentation dans Eclipse (menu Project/Generate Javadoc) − Possibilité pour ceux qui le souhaitent de générer avec Maven (mvn site) 1. http://checkstyle.sourceforge.net/ 16/32 01/2019 Denis Conan CSC4102 : Qualité du code JAVA et introduction aux idiomes JAVA
2.3.1 Documentation Javadoc I Classe eu.telecomsudparis.csc4102.util.Datutil 1 /* * 2 * compare la date avec aujourd ’ hui : vrai si aujourd ’ hui . 3 * 4 * @param date 5 * la date a comparer avec le jour courant . 6 * @return vrai si aujourd ’ hui 7 * @throws I l l e g a l A r g u m e n t E x c e p t i o n si { @code date } est { @code null } 8 */ 9 public static boolean da te Est Au jou rd hui ( final LocalDate date ) { 10 if ( date == null ) { 11 throw new I l l e g a l A r g u m e n t E x c e p t i o n ( " date ␣ == ␣ null " ) ; 12 } 13 return date . equals ( aujourdhui () ) ; 14 } http://www.oracle.com/technetwork/java/javase/documentation/index-jsp-135444.html [Javadoc, JSE8] Notez que les commentaires Javadoc commencent par la chaı̂ne de caractères « /** » 17/32 01/2019 Denis Conan CSC4102 : Qualité du code JAVA et introduction aux idiomes JAVA
2.3.1 Documentation Javadoc II Vérification de la présence et correction « du Javadoc » avec CheckStyle 1 /* * 2 * Bla bla . 3 * @param e explication sur e . 4 */ 5 public void ajouter ( final int a , final int b ) { }; • « Balise javadoc @param inutilisé pour ‘e’. » • « Balise javadoc @param manquante pour ‘a’. » • « Balise javadoc @param manquante pour ‘b’. » 18/32 01/2019 Denis Conan CSC4102 : Qualité du code JAVA et introduction aux idiomes JAVA
2.3.2 Autres exemples I Aussi présenté dans [Bloch, 2008], Item 8 1 private int Valeur ; 2 public void MaMethode () { // ... • « Le nom ‘Valeur’ n’est pas conforme à l’expression ‘ˆ[a-z][a-zA-Z0-9]*$’. » • « Le nom ‘MaMethode’ n’est pas conforme à l’expression ‘ˆ[a-z][a-zA-Z0-9]*$’. » Standard d’écriture lexicale du code JAVA Paquetage seance5.mediathequesimplifie, eu.telecomsudparis.csc4102 Classe, interface Timer, HashMap, HttpServlet Méthode, attribut remove, ensureCapacity, getVote Constantes MIN VALUE, NEGATIVE INFINITY Variable locale i, somme, sommeVote Paramètre de T, V, K, T1 type 19/32 01/2019 Denis Conan CSC4102 : Qualité du code JAVA et introduction aux idiomes JAVA
2.3.2 Autres exemples II 1 public int b ; « La variable ‘b’ devrait être privée et avoir des accesseurs. » 1 public String toString () { return " A ␣ [ Valeur = " + valeur + " ] " ; } « overrides java.lang.Object.toString » • Ajouter @Override, sinon on risque une erreur difficile à trouver − P.ex. si on écrit « public boolean equals(Document obj) {...} » 20/32 01/2019 Denis Conan CSC4102 : Qualité du code JAVA et introduction aux idiomes JAVA
2.3.2 Autres exemples III 1 public int ajouter ( int a , int b ) { 2 return a + b ; 3 } « Le paramètre a devrait être final. » « Le paramètre b devrait être final. » • Pour se prémunir du changement de la valeur des arguments en oubliant que ces changements sont oubliés en fin de méthode − On écrira « public int ajouter(final int a, final int b)... » 21/32 01/2019 Denis Conan CSC4102 : Qualité du code JAVA et introduction aux idiomes JAVA
3 Définition idiome/patron d’implémentation Une des premières occurrences du terme • « Idioms — Because collections are so powerful, there are a small set of standard tricks that experienced Smalltalkers know to play with them. If you are reading code that uses one of these idioms, you may be puzzled at first. This section introduces the problems you can solve using collections in unusual ways. » [Beck, 1995] « By idioms, we mean some (often unusual) combination of abstractions specific to some programming language, which are used for implementing some other abstraction that is not part of this language. » [Czarnecki and Eisenecker, 2000] Notre position dans ce module : les idiomes permettent d’approfondir des concepts de base importants Livre de référence pour JAVA : « Effective Java, 2nd Edition » [Bloch, 2008] • Dans CSC4102, à titre d’exemple, parcours de quelques idiomes − Méthodes de la classe Object communes à toutes les classes − Plus dans le document « pour aller plus loin » de cette séance 22/32 01/2019 Denis Conan CSC4102 : Qualité du code JAVA et introduction aux idiomes JAVA
4 Idiomes JAVA, premiers exemples, méthodes de la classe Object communes à tous les objets 4.1 Méthode equals 4.2 Méthode hashCode 4.3 Méthode toString 4.4 Les autres méthodes de la classe Object 23/32 01/2019 Denis Conan CSC4102 : Qualité du code JAVA et introduction aux idiomes JAVA
4.1 Méthode equals Repris de l’Item 8 de [Bloch, 2008] Contrat de la méthode equals [Class Object, JSE8] • Prend en argument un Object : utiliser @Override pour éviter une erreur • Reflexive : for any non-null reference value x, x.equals(x) should return true • Symmetric : for any non-null reference values x and y, x.equals(y) should return true if and only if y.equals(x) returns true • Transitive : for any non-null reference values x, y, and z, if x.equals(y) returns true and y.equals(z) returns true, then x.equals(z) should return true • Consistent : for any non-null reference values x and y, multiple invocations of x.equals(y) consistently return true or consistently return false, provided no information used in equals comparisons on the objects is modified • For any non-null reference value x, x.equals(null) should return false Cf. « pour aller plus loin » avec des exemples de non-respect des propriétés • Conclusion : Utilisez l’assistant Eclipse pour générer les méthodes equals + Choisir les attributs définissant de manière unique un objet de la classe 24/32 01/2019 Denis Conan CSC4102 : Qualité du code JAVA et introduction aux idiomes JAVA
4.2 Méthode hashCode Repris de l’Item 9 de [Bloch, 2008] Redéfinir equals impose de redéfinir aussi hashCode car ∀o1 , o2 : o1 .equals(o2 ) =⇒ o1 .hashCode() = o2 .hashCode() Classe seance7.methodescommunesatouslesobjets.CleErronee 1 public final class CleErronee { 2 private String nom , prenom ; 3 public CleErronee ( final String n , final String p ) { nom = n ; prenom = p ;} 4 @Override public boolean equals ( final Object obj ) { // ... avec nom , prenom 5 // pas de methode hashCode () Classe seance7.methodescommunesatouslesobjets.ExempleCleSansHashCode 1 Client c = new Client ( " nom " , " prenom " , " adresse " ) ; 2 CleErronee c1 = new CleErronee ( " nom " ," prenom " ) , c2 = new CleErronee ( " nom " ," prenom " ) ; 3 Map < CleErronee , Client > d = new HashMap < CleErronee , Client >() ; d . put ( c1 , c ) ; 4 System . out . print ( c1 . hashCode () + " != " + c2 . hashCode () + " ,␣ " + d . get ( c2 ) ) ; Résultat de l’exécution : 366712642!=1829164700, null • As much as is reasonably practical, the hashCode method defined by class Object does return distinct integers for distinct objects. (This is typically implemented by converting the internal address of the object into an integer.) [Class Object, JSE8] 25/32 01/2019 Denis Conan CSC4102 : Qualité du code JAVA et introduction aux idiomes JAVA
4.3 Méthode toString Repris de l’Item 10 de [Bloch, 2008] Toujours redéfinir toString • Par défaut, nom de la classe + @ + représentation hexadécimale non signée de la valeur donnée par hashCode() Classe seance7.methodescommunesatouslesobjets.EnfantAvecToString 1 public class E n f a n t A v e c T o St r in g extends P ar e nt A v ec T oS t r in g { 2 private int b ; 3 public E n f a n t A v e c T o S t r i n g ( final int a , final int b ) { super ( a ) ; this . b = b ;} 4 @Override public String toString () { 5 return " E n f a n t A v e c T o S t r in g ␣ [ b = " + b + " ,␣ toString () = " + super . toString () + " ] " ;}} Classe seance7.methodescommunesatouslesobjets.ExemplesToString 1 E nf a n tS a n s T o S t r i n g e = new E nf a n tS a ns T o St r in g (1 ,2) ; 2 System . out . println ( e + " ,␣ " + Long . toHexString ( e . hashCode () ) ) ; 3 E nf a n tA v e c T o S t r i n g e2 = new E nf a n tA v ec T o St r in g (1 ,2) ; 4 System . out . println ( e2 ) ; Résultat de l’exécution : seance7.methodescommunesatouslesobjets.EnfantSansToString@15db9742, 15db9742 EnfantAvecToString [b=2, toString()=ParentAvecToString [a=1]] 26/32 01/2019 Denis Conan CSC4102 : Qualité du code JAVA et introduction aux idiomes JAVA
4.3.1 Assoc. bidirectionnelles et toString Classe seance7.methodescommunesatouslesobjets.associationbidirectionnelle.Ensemble Ensemble 1 private List < Element > elems ; 2 public Ensemble ( final int v ) { elems = new ArrayList < Element >() ; } 3 public void ajouterUnA ( final int v ) { elems . add ( new Element ( this , v ) ) ; } * Élément 4 @Override // redefinition : contenu toString () de elems 5 public String toString () { return " Ensemble ␣ [ elems = " + elems + " ] " ;}} Classe seance7.methodescommunesatouslesobjets.associationbidirectionnelle.Element 1 private int v ; 2 private Ensemble ens ; 3 public Element ( final Ensemble ens , final int v ) { this . v = v ; this . ens = ens ;} 4 @Override // redefinition : contenu de v et toString () de ens 5 public String toString () { return " Element ␣ [ v = " + v + " ,␣ ens = " + ens + " ] " ;}} Classe seance7.methodescommunesatouslesobjets.associationbidirectionnelle.ExempleBoucleInfinie 1 Ensemble b = new Ensemble (1) ; b . ajouterUnA (2) ; System . out . println ( b ) ; Résultat de l’exécution : Exception in thread "main" java.lang.StackOverflowError Il faut éviter la formation du cycle d’appels • Ensemble::toString doit utiliser des getters de Element au lieu d’utiliser Element::toString 27/32 01/2019 Denis Conan CSC4102 : Qualité du code JAVA et introduction aux idiomes JAVA
4.3.2 Énumérations avec toString * Classe seance7.methodescommunesatouslesobjets.EnumerationSansRedefinitionDeToString 1 public enum E n u m e r a t i o n S a n s R e d e f i n i t i o n D e T o S t r i n g { Premier , Deuxieme ;} Classe seance7.methodescommunesatouslesobjets.EnumerationAvecRedefinitionDeToString 1 public enum E n u m e r a t i o n A v e c R e d e f i n i t i o n D e T o S t r i n g { 2 Premier ( " Mon ␣ premier " ) , Deuxieme ( " Mon ␣ second " ) ; 3 private String libelle ; 4 // notez que le constructeur est prive 5 private E n u m e r a t i o n A v e c R e d e f i n i t i o n D e T o S t r i n g ( final String libelle ) { 6 this . libelle = libelle ; } 7 @Override // redefinition : le libelle , plutot que le nom de l ’ objet 8 public String toString () { return libelle ; } } Classe seance7.methodescommunesatouslesobjets.ExemplesToStringEnumeration 1 System . out . println ( E n u m e r a t i o n S a n s R e d e f i n i t i o n D e T o S t r i n g . Deuxieme ) ; 2 System . out . println ( E n u m e r a t i o n A v e c R e d e f i n i t i o n D e T o S t r i n g . Deuxieme ) ; Résultat de l’exécution : Deuxieme Mon second 1. Le constructeur de EnumerationAvecRedefinitionDeToString est private car il n’existe aucune raison de le mettre public puisqu’aucun nouvel objet n’est instanciable depuis l’extérieur 28/32 01/2019 Denis Conan CSC4102 : Qualité du code JAVA et introduction aux idiomes JAVA
4.4 Les autres méthodes de la classe Object [Class Object, JSE8] getClass : Returns the runtime class of this object Méthodes pour la concurrence : • S’utilise avec la directive Synchronized (hors programme CSC4102) • notify : wakes up a single thread that is waiting on this object’s monitor − If any threads are waiting on this object, one of them is chosen to be awakened − The choice is arbitrary and occurs at the discretion of the implementation • notifyAll : wakes up all threads that are waiting on this object’s monitor • wait : causes the current thread to wait until either another thread invokes the notify() method or the notifyAll() method for this object, or a specified amount of time has elapsed 29/32 01/2019 Denis Conan CSC4102 : Qualité du code JAVA et introduction aux idiomes JAVA
5 Mise en pratique en TP (2h) + HP (3h) Qualité du code avec FindBugs/SpotBugs puis CheckStyle Idiomes : programmation des méthodes communes à tous les objets Continuation du développement de l’application de l’étude de cas Organisation du TP (2h) • 1h : Travail en binôme-projet • 2 × 5mn : Évaluation croisée entre binômes-projets + bilan intermédiaire collectif • Reste de la séance : continuation des travaux Rendu de la séance en HP (3h) : PDF + JAVA, avec « seance7 » dans « sprint2 » Compléments « Pour aller plus loin » avec d’autres idiomes 30/32 01/2019 Denis Conan CSC4102 : Qualité du code JAVA et introduction aux idiomes JAVA
Autres exemples Références I Ayewah, N. and Pugh, W. (2010). The Google FindBugs Fixit. In Proc. of the 19th International Symposium on Software Testing and Analysis, pages 241–252, Trento, Italy. Ayewah, N., Pugh, W., Hovemeyer, D., Morgenthaler, D., and Penix, J. (2008). Using Static Analysis to Find Bugs. IEEE Software, 25(5) :22–29. Beck, K. (1995). Smalltalk Best Practice Patterns Volume 1 : Coding. Prentice Hall. Bloch, J. (2008). Effective Java, 2nd Edition. Prentice Hall. Class Object (JSE8). Javadoc of the class Object of JAVA SE 8. https: //docs.oracle.com/javase/9/docs/api/index.html?java/lang/Object.html. 31/32 01/2019 Denis Conan CSC4102 : Qualité du code JAVA et introduction aux idiomes JAVA
Autres exemples Références II Czarnecki, K. and Eisenecker, U. (2000). Generative Programming : Methods, Tools, and Applications. Addison-Wesley. Javadoc (JSE8). Javadoc Tool. http://www.oracle.com/technetwork/java/javase/documentation/ index-jsp-135444.html. 32/32 01/2019 Denis Conan CSC4102 : Qualité du code JAVA et introduction aux idiomes JAVA
Vous pouvez aussi lire