IV. Méthodes Void▲
Nous avons toujours écrit de petits programmes n’ayant qu’une seule classe et une seule méthode (main).
Dans ce chapitre, nous allons voir comment on organise un long programme avec de multiples classes et méthodes.
Nous allons nous intéresser à la classe Math, qui fournit les méthodes des principales opérations mathématiques.
IV-A. Méthodes de la classe Math▲
En mathématiques, vous avez sûrement déjà vu les fonctions sin et log et vous avez appris à résoudre les opérations telles que sin(∏/2) et log(1/x).
D’abord, on résout l’expression entre parenthèses, qui est appelée argument de la fonction. Ensuite, nous résolvons la fonction elle-même, en la rentrant dans une calculatrice par exemple.
Ce processus peut être répété pour résoudre des expressions plus complexes telles que log(1/sin(∏/2)).
D’abord nous résolvons l’argument de la fonction interne puis la fonction elle-même.
La libraire Java inclut la classe Math fournissant la plupart des opérations mathématiques. Math appartient au paquet java.lang, donc pas besoin de l’importer. Vous pouvez utiliser ou invoquer les méthodes Math comme cela :
double
root =
Math.sqrt
(
17.0
);
double
angle =
1.5
;
double
height =
Math.sin
(
angle);
La première ligne définit root comme la racine carrée de 17.
La troisième ligne permet de trouver le sinus de 1,5 (la valeur de angle).
Les arguments des fonctions trigonométriques sin, cos et tan doivent être en radian.
Pour convertir des degrés en radian, vous pouvez diviser par 180 et multiplier par ∏. Pour vous aider, la classe Math contient la constante double nommée PI qui contient une approximation de ∏ :
double
degrees =
90
;
double
angle =
degrees /
180.0
*
Math.PI;
Attention, Pi est en majuscule. Java ne reconnaîtra pas Pi, pi ou pie. Aussi, PI étant une variable et non une méthode, pas besoin d’utiliser de parenthèses. Cela est aussi valable pour la constante Math.E qui est une approximation du nombre d’Euler.
La conversion en radian et inversement est une opération courante, ainsi la classe Math apporte des méthodes pour le faire à votre place.
double
radians =
Math.toRadians
(
180.0
);
double
degrees =
Math.toDegrees
(
Math.PI);
Une autre méthode très utile est round, qui effectue l’arrondi d’une valeur décimale vers l’entier le plus proche et renvoie une valeur de type long. Le type long est comme le type int mais plus gros.
Plus précisément, le int utilise 32bits, ainsi sa valeur maximale est 2 31-1, soit environ 2 billions. Le long utilise 64bits, ainsi sa valeur maximale est 2 63 -1, soit environ 9 quintillions.
long
x =
Math.round
(
Math.PI *
20.0
);
Le résultat est 63 (arrondi supérieur de 62,8319).
Prenez une minute pour lire la documentation des différentes méthodes de la classe Math. Le moyen le plus simple pour trouver la documentation des classes Java est d’effectuer une recherche telle que "java " et le nom de votre classe.
IV-B. Composition revisitée▲
Comme pour les fonctions mathématiques, les méthodes Java peuvent être composées. Cela signifie que l’on peut utiliser une expression comme la partie d’une autre. Par exemple, vous pouvez utiliser n’importe quelles expressions comme argument d’une méthode :
double
x =
Math.cos
(
angle +
Math.PI /
2.0
);
Ici, on divise Math.Pi par 2, puis on ajoute le résultat à angle, enfin on effectue le cosinus de la somme.
On peut également prendre le résultat d’une méthode et le passer en argument d’une autre :
double
x =
Math.exp
(
Math.log
(
10.0
));
En Java, la méthode log utilise toujours la base e. Dans cet exemple, on trouve le log de 10 que l’on passe à la fonction exponentielle. Le résultat est assigné à x.
double
x =
Math.pow
(
2.0
, 10.0
);
Certaines méthodes Math utilisent plus d’un argument. Par exemple, Math.pow prend 2 arguments et élève le premier à la puissance du second.
Cette ligne de code assigne à la variable x la valeur de 1024 :
Quand on utilise des méthodes Math, on commet souvent l’erreur d’oublier la classeMath.
Par exemple, si vous essayez utiliser pow(2.0 , 10.0), vous aurez un message du type :
2.
3.
4.
File
:
Test.java [line:5
]
Error : cannot find symbol
symbol : method pow
(
double
, double
)
location : class
Test.java
Le message "cannot find symbol" est déroutant, mais la dernière ligne fournit une information importante. Le compilateur recherche pour pow dans la même classe, celle qui l’utilise étant Test.
Si vous ne spécifiez pas le nom de la classe, le compilateur regarde dans la classe courante.
IV-C. Ajout des nouvelles méthodes▲
Vous avez probablement deviné que l’on peut définir plus d’une méthode dans une classe. Ceci est un exemple :
2.
3.
4.
5.
6.
7.
8.
9.
10.
public
class
NewLine{
public
static
void
newLine
(
){
System.out.println
(
) ;
}
public
static
void
main
(
String[] args){
System.out.println
(
"First line."
) ;
newLine
(
) ;
System.out.println
(
"Seconde line."
) ;
}
}
Le nom de la classe est NewLine. Par convention de nommage, les noms de classes commencent par une majuscule. NewLine contient 2 méthodes, newLine et main. Souvenez-vous que Java est sensible à la casse, ainsi NewLine et newLine ne sont pas les mêmes.
Les noms des méthodes peuvent commencer par une minuscule et utiliser le "camel case", ce qui donne un joli nom pour jammingWordsTogetherLikeThis . Vous pouvez utiliser n’importe quel nom pour vos méthodes, exceptés « main » et les mots clefs Java.
Les méthodes newLine et main sont public, ce qui signifie qu’elles peuvent être utilisées par une autre classe. Elles sont toutes les 2 static, nous expliquerons cela plus bas. Et elles sont également void, ce qui signifie qu’elles ne renvoient pas de résultat (contrairement aux méthodes de la classe Math, par exemple).
Les parenthèses après un nom de méthode contiennent une liste de variables, appelées paramètres, quand une méthode l’enregistre cela devient un argument.
main a un seul paramètre, appelé args, qui est du type String[]. Ce qui signifie que l’invocation de main doit être pourvue d’un tableau de string. (Nous verrons les tableaux dans le prochain chapitre).
Ainsi, newLine n’a pas de paramètres, elle ne nécessite pas d’argument, comme indiqué lorsque qu’elle est invoquée dans main. Et parce que newLine est de la même classe que main, nous n’avons pas besoin de préciser le nom de la classe.
Le résultat écran du programme est :
First line.
Second line.
Remarquez l’espace entre les 2 lignes. Si vous voulez plus d’espace entre elles, vous pouvez invoquer plusieurs fois la même méthode :
2.
3.
4.
5.
6.
7.
public
static
void
main
(
String[] args){
System.out.println
(
"First line."
) ;
newLine
(
) ;
newLine
(
) ;
newLine
(
) ;
System.out.println
(
"Seconde line."
) ;
}
Ou nous pouvons écrire une nouvelle méthode faisant apparaître 3 lignes vides :
2.
3.
4.
5.
6.
7.
8.
9.
10.
public
static
void
threeLine
(
){
newLine
(
) ;
newLine
(
) ;
newLine
(
) ;
}
public
static
void
main
(
String[] args){
System.out.println
(
"First line."
) ;
threeLine
(
) ;
System.out.println
(
"Seconde line."
) ;
}
Vous pouvez invoquer la même méthode plus d’une fois et une méthode peut en invoquer une autre.
Dans cet exemple, main invoque threeLine, et threeLine invoque newLine().
Les débutants se demanderont pourquoi nous devons créer une nouvelle méthode. Il y a plusieurs raisons à cela, mais cet exemple n’en expose qu’une partie d’entre elles :
- créer de nouvelles méthodes vous permet de donner un nom à un groupe de déclarations, ce qui rend le code plus facile à lire et comprendre ;
- introduire de nouvelles méthodes permet de réduire la taille du programme en éliminant la répétitivité du code. Par exemple, pour afficher 9 nouvelles lignes consécutives, vous pouvez invoquer threeLine 3 fois ;
- une technique courante de résolution de bug est de dissocier la tâche en sous-programmes. Cette méthode permet de cibler séparément chaque problème, et leur somme donne une solution complète.
IV-D. Flux d’exécution▲
Réutilisons le code de la section précédente afin d’avoir un programme complet ressemblant à ceci :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
public
class
NewLine{
public
static
void
newLine
(
){
System.out.println
(
) ;
}
public
static
void
threeLine
(
){
newLine
(
) ;
newLine
(
) ;
newLine
(
) ;
}
public
static
void
main
(
String[] args){
System.out.println
(
"First line."
) ;
threeLine
(
) ;
System.out.println
(
"Seconde line."
) ;
}
}
Quand vous regardez la définition d’une classe contenant plusieurs méthodes, il est tentant de la lire de haut en bas. Mais c’est confus, car cela ne correspond pas au flux d’exécution du programme.
L’exécution commence toujours par la méthode main, peu importe où elle se situe dans le fichier source. Les instructions déclarées sont exécutées une par une, dans l’ordre, jusqu’à l’invocation d’une méthode.
Au lieu d’aller à l’instruction suivante, vous allez à la première ligne d’instruction de la méthode invoquée, vous exécutez toutes les instructions présentes, puis vous revenez où vous vous étiez arrêté.
Cela semble assez simple, mais rappelez-vous qu’une méthode peut en invoquer une autre.
Au milieu du main, nous allons exécuter les instructions présentes dans threeLine. Quand nous exécutons threeLine, nous allons exécuter newLine. Puis newLine invoque println, ce qui provoque un retour à la ligne.
Heureusement, Java est fort pour garder une trace des méthodes en exécution.
Ainsi, quand println est terminé, il revient dans newLine, quand newLine est terminé, il revient à threeLine et quand threeLine est terminé, il revient au main.
En résumé quand vous lisez un programme, ne le faites pas de haut en bas, suivez le flux d’exécution !
IV-E. Paramètres et arguments▲
Certaines des méthodes que nous avons utilisées nécessitent des arguments, qui sont des valeurs chargées quand nous invoquons la méthode.
Par exemple, pour trouver le sinus d’un nombre, nous devons charger le nombre, or sin prend comme argument un double. Pour faire apparaître le message, nous devons charger le message, or println prend un string.
Quand on utilise une méthode, on charge des arguments. Quand vous écrivez une méthode, vous nommez les paramètres. La liste de paramètres indique quels sont les arguments requis. La classe suivante vous montre un exemple :
2.
3.
4.
5.
6.
7.
8.
9.
public
class
PrintTwice{
public
static
void
printTwice
(
String s){
System.out.println
(
s) ;
System.out.println
(
s) ;
}
public
static
void
main
(
String[] args){
printTwice
(
"Don't make me say this twice"
) ;
}
}
printTwice attend un paramètre nommé s de type String. Quand nous invoquons printTwice, nous chargeons un argument du type String.
Avant que la méthode s’exécute, l’argument est assigné à un paramètre. Dans cet exemple, l’argument "Don't make me say this twice!" est assigné au paramètre s.
Ce processus est appelé transfert de paramètre parce que la valeur est passée de l’extérieur de la méthode vers l’intérieur. Un argument peut être de n’importe quel type d’expression, donc si vous avez une variable de type String, vous pouvez l’utiliser comme argument :
La valeur chargée comme argument doit avoir le même type que le paramètre. Par exemple, si vous essayez :
printTwice
(
17
); // syntax error
Vous allez avoir un message d’erreur tel que :
2.
3.
4.
5.
File : Test.java [line : 10
]
Error : method printTwice in class
Test cannot be applied to given types ;
required : java.lang.string
found : int
reason : actual argument int
cannot be converted to java.lang.String by method inversion conversion
Quelquefois, Java peut convertir automatiquement un argument d’un type en un autre.
Par exemple, Math.sqrt requiert un double, mais si vous invoquez Math.sqrt(25), la valeur integer(25) est automatiquement convertie en un type décimal de valeur 25.0.
Mais dans le cas printTwice, Java ne peut pas convertir l’integer 17 en String. De même, dans printTwice, il n’y a pas d’arguments. Cette variable appartient à main.
Les paramètres et les autres variables existent uniquement dans leur méthode. À l’intérieur de main, il n’y a rien d’autre que s. Si vous essayez de l’utiliser ici, vous aurez une erreur de compilation. Similairement, à l’intérieur de printTwice il n’y a rien d’autre que argument. Cette variable appartenant à main.
Parce que les variables n’existent que dans les méthodes qui les définissent, elles se font appeler variables locales.
IV-F. Multiples paramètres▲
Dans cet exemple, la méthode prend 2 paramètres :
2.
3.
4.
5.
public
static
void
printTime (
int
hour, int
minute){
System.out.println
(
hour) ;
System.out.println
(
" :"
) ;
System.out.println
(
minute) ;
}
Dans la liste de paramètres, on est facilement tenté d’écrire :
2.
public
static
void
printTime
(
int
hour, minute) {
...
Mais ce format (sans le second int) est uniquement valable pour la déclaration des variables. Dans la liste des paramètres, on doit spécifier le type de chaque variable séparément.
2.
3.
int
hour =
11
;
int
minute =
59
;
printTime
(
hour, minute);
Une erreur courante est de déclarer les types d’arguments comme cela :
2.
3.
int
hour =
11
;
int
minute =
59
;
printTime
(
int
hour, int
minute); // syntax error
Ceci est une erreur de syntaxe, le compilateur voit int hour et int minute comme des déclarations de variables, non comme des expressions. Vous n’avez pas à déclarer le type des arguments s’ils sont simplement des entiers (int) :
printTime
(
int
11
, int
59
); // syntax error
IV-G. Pile de diagrammes▲
Regroupons les fragments de code de la section précédente, ainsi la définition complète de la classe devient :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
public
class
PrintTime{
public
static
void
printTime (
int
hour, int
minute){
System.out.println
(
hour) ;
System.out.println
(
" :"
) ;
System.out.println
(
minute) ;
}
public
static
void
main
(
String[] s){
int
hour =
11
;
int
minute =
59
;
printTime
(
hour,minute) ;
}
}
printTime a 2 paramètres, appelés hour et minute. Et main possède deux variables aussi nommées hour et minute. Bien qu’elles aient les mêmes noms, ces variables ne sont pas les mêmes. Hour dans printTime et hour dans main font référence à différents lieux de stockage, et peuvent avoir des valeurs différentes.
Par exemple, vous pouvez invoquer printTime comme ceci :
2.
3.
Int hour =
11
;
int
minute =
59
;
printTime
(
hour +
1
, 0
) ;
Avant que cette méthode ne soit invoquée, Java évalue les arguments. Dans cet exemple, le résultat est 12 et 0. Puis ces valeurs sont renseignées comme paramètres.
Dans printTime, la valeur de hour est 12 et non pas 11, et la valeur de minute est 0 et non pas 59.
De plus, si printTime modifie l’un de ses paramètres, ce changement n’a aucun effet sur les variables.
Une technique pour garder une trace de tout, c’est de dessiner une pile de diagrammes, laquelle est un diagramme d’états (voir Section 2.3) montrant les méthodes d’invocation.
Pour chaque méthode une boîte, appelée frame contenant les paramètres et les variables des méthodes.
Le nom de la méthode apparaît en dehors du cadre, les variables et les paramètres apparaissent à l’intérieur.
|
Figure 4.1 : pile de diagrammes pour printTime. |
Comme pour les diagrammes d’états, les piles de diagrammes montrent les variables et les méthodes à un moment donné. La Figure 4.1 est une pile de diagrammes au début de la méthode printTime.
IV-H. Lecture de la documentation▲
L’un des avantages de Java est qu’il contient une vaste bibliothèque de classes et de méthodes. Mais avant de les utiliser, vous devez lire la documentation. Et parfois cela n’est pas facile.
Par exemple, allons regarder la documentation de Scanner qui est utilisé dans la section 3.2. Vous pouvez la trouver en effectuant la recherche suivante : "java scanner". La figure 4.2 montre une capture d’écran de la page.
La documentation pour les autres classes utilise un format similaire.
La première ligne est le paquet contenant la classe, telle que java.util.
La seconde ligne est le nom de la classe.
La section "Implemented Interfaces" liste certaines choses que Scanner peut faire ; nous n’en dirons pas plus là-dessus pour le moment.
La section suivante de la documentation est une description des possibilités de la classe et inclut des exemples de son utilisation.
Le texte peut être difficile à lire parce que les termes employés n’ont jamais été vus. Mais l’exemple est souvent très utile. Un bon moyen de commencer avec une nouvelle classe est de copier l’exemple dans un fichier test, d’essayer de le compiler et ensuite de l’exécuter.
L’un de ces exemples montre comment on peut utiliser un Scanner pour lire l’entée depuis un String au lieu d’utiliser System.in :
Après la description, les exemples de codes et d’autres détails, nous allons trouver les paragraphes suivants :
- Constructor summary :
manière de créer ou "construire", un Scanner ; - Method summary :
liste des méthodes que Scanner fournit ; - Constructor detail :
plus d’information sur la manière de créer un Scanner ; - Method detail :
plus d’information sur chaque méthode.
Par exemple, ceci est un résumé des informations pour nextInt :
2.
public
int
nextInt
(
)
Scans the next token of the input as an int
.
La première ligne de la méthode est la signature, qui spécifie le nom de la méthode, ses paramètres (aucun) et le type de retour (int). La ligne d’après est une description sommaire de ce qu’elle réalise.
La partie "Method detail" explique plus :
2.
3.
4.
5.
6.
7.
8.
9.
public
int
nextInt
(
)
Scans the next token of the input as an int
.
An invocation of this
method od the dorm nextInt
(
) behaves un exactly the same way as the invocation nextint
(
radix), where radix is the default
radix of this
scanner.
Returns :
the int
scanned for
the input
Throws :
InputMismatchException – if
the next token does not match the Integer regular expression, or is out of range
NoSuchElementException – if
input is exhauted
IllegalStateException – if
this
scanner is closed
La section "Returns" décrit le résultat quand la méthode se termine avec succès.
À contrario, la section "Throws" décrit les erreurs possibles et les exceptions résultantes. Les exceptions sont appelées à être "thrown", comme un arbitre jetant un drapeau ou un enfant faisant une crise jetant un objet.
Cela prend du temps pour se familiariser avec la documentation afin de savoir quelles sont les parties à ignorer. Mais le jeu en vaut la chandelle. Savoir ce qui est disponible dans une bibliothèque, nous évite de réinventer la roue. Et un peu de documentation peut vous épargner beaucoup de débogage.
IV-I. Rédaction de la documentation▲
Lorsque vous bénéficiez d’une bonne documentation, vous devez retourner l’ascenseur en écrivant à votre tour une bonne documentation. Une fonctionnalité sympathique du langage Java est sa capacité à générer sa documentation dans le code source. De cette façon, vous pouvez l’écrire au fur et à mesure. Ainsi, lorsqu’il y a des modifications, il est facile de garder une documentation à jour.
Si vous incluez de la documentation dans votre code source, il est facile de l’extraire automatiquement et de générer un joli HTML, en utilisant l’outil Javadoc.
Cet outil qui est inclus en standard dans les environnements de développement Java, est largement utilisé.
En effet, la documentation en ligne des librairies java est générée par Javadoc.
Javadoc scanne les fichiers sources en regardant les commentaires de documentation spécialement formatés, plus connus sous le nom de "javadoc comments". Ils commencent par ** (2 étoiles) et terminent par */ (une étoile et une barre oblique). Tout ce qui se situe entre ces 2 éléments est considéré comme de la documentation.
Ici une classe est définie avec deux commentaires javadoc, un pour la classe et un pour la méthode main.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
/**
* Exemple program that demonstrates print vs println
*/
public
class
Goodbye{
/**
* Printing is greeting
*/
public
statis void
main
(
String[] args){
System.out.print
(
"Goodbye, "
) ; //note the space System.out.println("cruel world") ;
}
}
Le commentaire de la classe explique son but.
Le commentaire de la méthode explique ce qu’elle réalise.
Notez que cet exemple inclut aussi un commentaire de ligne, commençant par //
En général, les commentaires de ligne sont de petites phrases pour aider à expliquer des parties complexes du programme. Ils sont destinés aux autres programmeurs lisant et maintenant le code source.
Par contraste, les commentaires Javadoc sont plus longs et souvent complets. Ils expliquent ce que chaque méthode réalise, mais ils omettent les détails concernant le fonctionnement des méthodes. Ils sont destinés aux personnes qui utilisent les méthodes sans regarder le code source.
Commentaires et documentation corrects sont essentiels pour faire un code source lisible. Et gardez à l’esprit que la personne qui relira votre code dans le futur et appréciera une bonne documentation pourra être vous !
IV-J. Vocabulaire▲
Argument :
valeur que l’on fournit lors de l’appel d’une méthode. Cette valeur doit avoir le même type que le paramètre correspondant.
Invoquer :
demander l’exécution d’une méthode. Communément appelé : appel
Paramètre :
partie d’information requise par une méthode avant de fonctionner. Les paramètres sont des variables. Ils contiennent des valeurs et ont des types.
Flux d’exécution :
ordre dans lequel Java exécute les méthodes et les instructions. Cela n’est pas obligatoirement de haut en bas et de gauche à droite.
Passage de paramètres :
processus qui assigne une valeur d’argument à une variable de paramètre.
Variable locale :
variable déclarée à l’intérieur d’une méthode. Les variables locales ne sont pas accessibles en dehors de leurs méthodes.
Pile de diagrammes :
représentation graphique des variables au fil des appels des méthodes. Les méthodes appelées sont empilées de haut en bas dans le flux d’exécution.
Cadre :
dans une pile de diagrammes, représentation des variables et des paramètres pour une méthode, avec leurs valeurs courantes.
Signature :
la première ligne d’une méthode la définit par son nom, son type de retour et ses paramètres.
Javadoc :
outil lisant le code source Java et générant la documentation au format HTML
Documentation :
commentaire décrivant les opérations techniques pour une classe ou une méthode.
IV-K. Exercices▲
Le code pour ce chapitre est présent dans le répertoire ch04 de ThinkJavaCode.
Avant de commencer les exercices, il est recommandé de compiler et d’exécuter les exemples.
Si vous n’avez pas déjà lu l’appendice A.4, je pense que c’est le bon moment. Il décrit la manière la plus efficace pour tester un programme prenant une entrée utilisateur et affichant le résultat sur une sortie spécifique.
Exercice 1
Cet exercice a pour but d’exercer la lecture de code source et d’être sûr que vous avez compris le flux d’exécution d’un programme avec plusieurs méthodes.
- Que s’affiche-t-il lors de l’exécution du programme ? Soyez précis sur la présence d’espace et de retour à la ligne.
Astuce : commencez par décrire ce que ping et baffle font lors de leur exécution. - Dessinez une pile de diagrammes qui montre l’état du programme lorsque ping est appelé pour la première fois.
- Que se passe-t-il si vous appelez baflle() à la fin de la méthode ping ? (vous verrez pourquoi dans le chapitre suivant).
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
public
static
void
zoop
(
) {
baffle
(
);
System.out.print
(
"You wugga "
);
baffle
(
);
}
public
static
void
main
(
String[] args) {
System.out.print
(
"No, I "
);
zoop
(
);
System.out.print
(
"I "
);
baffle
(
);
}
public
static
void
baffle
(
) {
System.out.print
(
"wug"
);
ping
(
);
}
public
static
void
ping
(
) {
System.out.println
(
"."
);
}
Exercice 2
Cet exercice est fait pour savoir si vous avez compris comment écrire et appeler une méthode utilisant des paramètres.
- Écrire la première ligne de la méthode appelée zool prenant 3 paramètres : un int et 2 String.
- Écrire une ligne de code appelant zool, en passant comme paramètres la valeur 11, le nom de votre premier animal et le nom de la rue où vous avez grandi.
Exercice 3
Dans cet exercice, nous utiliserons le code de l’exercice précédent et nous l’encapsulerons dans une méthode utilisant des paramètres.
- Écrivez une méthode appelée printAmerican prenant un jour, une date, un mois et une année en paramètres et affichez-les en format américain.
-
Testez votre méthode en l’appelant depuis main et en passant les arguments appropriés. La sortie doit ressembler à quelque chose comme cela. (excepté la date qui peut être différente).
Saturday, July
22
,2015
- Une fois que vous avez débogué printAmerican, écrivez une autre méthode appelée printEuropean affichant la date dans le format européen.