13. Les méthodes
Les méthodes sont des concepts de programmations communs à de très nombreux langages de programmation aussi connues sous le nom de fonctions. C'est un peu comme en mathématiques quand on déclare f(x) = x² : on déclare une fonction dont le nom est f, qui prend un paramètre x et qui renvoie la valeur x². Si j'appelle la fonction f avec le paramètre 5 je vais obtenir le résultat 25 : f(5) = 25 !
Sauf qu'ici, on va pouvoir créer des méthodes avec des noms plus explicites, des noms de variables plus élaborés, et des comportements plus complexes.
En Java, une méthode est définie par sa signature. C'est à dire que la fonction à un nom, un type de retour, des paramètres (optionnel). Si je veux par exemple définir une fonction équivalente à notre bonne vieille f(x) = x² en Java, je peux l'écrire :
long square(int value) {
return value * value;
}
C'est quasiment la même chose ! Sauf que maintenant ma méthode est nommée square, on sait qu'elle renvoie un long
et prend en paramètre un int value
.
Je peux l'appeler ainsi :
long result = square(5);
IO.println(result); // 25
Créer ses propres méthodes
En Java, il est possible de créer ses propres méthodes, comme je l'ai fait avec square au dessus.
Pour cela, il vous suffit de lui donner un type de retour (ou void s'il n'y en a pas), un nom et des paramètres.
Ensuite, le corps de la méthode peut contenir du code comme on l'a toujours fait jusque là !
Tenez, par exemple on peut faire une méthode qui renvoie un boolean
pour savoir si un nombre est pair :
Java1boolean isOddNumber(int n) {2if (n % 2 == 0) { // je vous avais dit que le modulo serait utile !3return true;4} else {5return false;6}7}
Et là, les super-malins pourraient remarquer que l'expression n % 2 == 0
renvoie déjà un boolean
, et que le if
pourrait être simplifié :
Java1boolean isOddNumber(int n) {2return n % 2 == 0; // ça marche aussi3}
Le mot clé return
Ça ne vous a probablement pas échappé, un nouveau mot clé à fait son apparition : return
. C'est un mot clé utilisé dans les méthodes pour définir la valeur que la méthode va retourner (son résultat).
Une méthode ne renverra une valeur que si elle a un type de retour différent de void
(qui signifie l'absence de type). En l'occurence, dans les deux exemples précédents, ma méthode long square(int value)
a un type de retour long
donc elle retourne une valeur de type long
. Ma méthode boolean isOddNumber(int n)
retourne un booléen avec return n % 2 == 0
qui renvoie soit true, soit false.
Tout code écrit après un return
ne sera pas exécuté puisque ce mot clé permet d'arrêter l'exécution de la méthode pour renvoyer immédiatement son résultat.
Java1boolean isOddNumber(int n) {2if (n % 2 == 0) {3return true; // fin de la méthode4IO.println("Coucou !"); // ne sera jamais exécuté5} else {6return false; // fin de la méthode7IO.println("Coucou !"); // ne sera jamais exécuté8}9}
Dans ma méthode isOddNumber, j'ai deux fins alternatives : soit le nombre est pair, soit il ne l'est pas. Suivant quelle branche du if
est exécutée, je renverrai true ou false mais je ne verrai jamais Coucou ! s'afficher dans la console !
Pour éviter les faux espoirs, le compilateur Java ne vous permettra pas d'écrire ça, il affichera une erreur Unreachable statement pour dire que l'instruction est impossible à atteindre.
Dans une méthode avec un type de retour void
, ce mot clé peut être utilisé sans expliciter de valeur, dans le but de quitter la méthode. Il est implicitement présent à la fin de chaque méthode mais vous pouvez le rajouter vous même si vous le souhaitez !
Ça pourra être utile dans certains cas comme par exemple :
Java1void drinkAlcohol(int age) {2if (age < 18) {3IO.println("Cannot dring before being 18!");4return; // on interromp l'exécution5}67IO.println("Stay safe!");8}
Dans cet exemple, le fait d'interrompre l'exécution revient au même que d'écrire :
Java1void drinkAlcohol(int age) {2if (age < 18) {3IO.println("Cannot dring before being 18!");4} else {5IO.println("Stay safe!");6}7}
La méthode main
Eh oui ! Si on regarde attentivement, notre fameux main est une méthode ! Mais si ! Regardez :
Java1void main() { }
C'est une méthode qui ne prend pas de paramètres, et renvoie void
. C'est à dire qu'elle ne renvoie rien du tout ! Ce n'est d'ailleurs pas la seule méthode que vous utilisiez ! C'est aussi le cas de println()
.
Le cas de main est un peu spécial puisque comme évoqué dans la partie explications sur le Hello World, il est le point d'entrée du programme. Sans lui, pas d'exécution du programme !
Et pour boucler la boucle, on peut même relier toutes ces informations avec le concept précédemment abordé des expressions et instructions. Car si une méthode renvoie void
, c'est une instruction, sinon, c'est une expression ! OMG TOUT EST LIÉ ! (oui)
La portée des variables
Les variables que vous déclarez dans une méthode ne sont visible que dans cette méthode. On dit qu'elle sont locales à la méthode.
Java1void main() {2String texte = "Coucou !";3IO.println(texte);4afficher();5}67void afficher() {8IO.println(texte); // ça compile pas !9}
Ici, le programme ne compilera pas car la variable texte utilisée dans la fonction afficher() n'existe pas. Elle est déclarée dans la méthode main, et les variables déclarées dans une méthode ne sont visibles que depuis la méthode en question.
Si je veux utiliser ma méthode afficher pour afficher des variables de mon main, il faut que je lui passe en paramètre :
Java1void main() {2String texte = "Coucou !";3IO.println(texte); // affiche bien Coucou !4afficher(texte); // affiche bien Coucou !5}67void afficher(String str) { // maintenant la méthode a un paramètre qui s'appelle str8IO.println(str);9}
Je rajoute un paramètre entre les parenthèses de la méthode afficher.
Ainsi, on réalise que les paramètres d'une méthode peuvent avoir n'importe quel nom, ils sont perçu comme une variable dont la valeur est attribuée par celui qui appelle la méthode. En l'occurence ici, c'est le main qui appelle afficher avec la variable texte en paramètre.
Cette notion de portée des variables est applicable de façon générale à n'importe quel bloc de code. Il faut que la variable soit déclarée pour être utilisée, et il faut qu'elle soit utilisée dans l'enceinte de sa portée :
Java1void main() {2int a = 5;34for (int i = 0; i < 10; i++) {5IO.println(a);6a = a + i;7}89IO.println(a);10IO.println(i); // compile pas car la variable i n'est visible que dans la boucle for11}
Si on veut absolument rendre ce code possible, il faut que la variable i soit déclarée en dehors de la boucle for, à la hauteur du main, exactement comme la variable a :
Java1void main() {2int a = 5;3int i = 0;45for (; i < 10; i++) { // ici je n'ai pas besoin de redéclarer i dans les parenthèses6IO.println(a);7a = a + i;8}910IO.println(a);11IO.println(i); // compile !12}
Les méthodes utiles au début
Pour l'instant, vous ne faites qu'utiliser IO.println()
, qui est une méthode fournie par le JDK mais c'est loin d'être la seule méthode disponible, et certaines seront utiles pour plus tard alors notons les ici pour pouvoir y revenir si nécessaire :
void IO.println() - affiche une nouvelle ligne dans la consolevoid IO.println(Object obj) - affiche le paramètre donné dans la console puis saute une lignevoid IO.print(Object obj) - affiche le paramètre donné dans la console sans sauter de lignesString IO.readln() - lis une ligne entrée par utilisateur sur la consoleString IO.readln(String prompt) - affiche le paramètre donné dans la console puis lis une ligne entrée par utilisateur sur la console
Java1void main() {2String name = IO.readln("Entrez votre nom : ");3String age = IO.readln("Entrez votre age : ");45IO.println("Bonjour " + name + ". Tu as " + age + " ans !");6}
Bonus
Comme vous l'avez peut-être remarqué, dans mon exemple juste au dessus, je demande à l'utilisateur d'entrer son age, et il est considéré comme String
dans le programme. Ce n'est pas une bonne idée car si j'ai besoin de faire des calculs avec cette valeur, j'ai besoin que ce soit un int
... Alors je vous donne une dernière méthode pratique pour palier à ce genre de soucis à l'avenir : Integer.parseInt(String s)
, qui permet de transformer la String s
donnée en paramètre en int
!
Il faut faire attention à ne pas lui donner n'importe quoi par contre, sinon le programme va planter :
Java1int a = Integer.parseInt("35"); // renvoie un int 352int b = Integer.parseInt("Coucou !") // apocalypse
En cas d'apocalypse, le programme va vous afficher un truc du genre :
Exception in thread "main" java.lang.NumberFormatException: For input string: "Coucou !", qu'il faut comprendre comme un message d'insulte pour ne pas lui avoir donné une valeur qu'il peut convertir.
Ranger son code
Parfois quand notre programme devient complexe, il convient de le découper en différentes méthodes pour simplifier la lecture et la maintenabilité du programme.
Par exemple, imaginons un jeu quelconque. Les règles sont complexes, le jeu est compliqué à coder et il serait de la folie de coder tout le programme dans le main !
Au lieu de ça, le main pourrait ressembler à ça :
Java1void main() {2GameState gameState = initGameState(); // initialise la partie34boolean gameOver = false;56while(!gameOver) { // tant que la partie n'est pas finie7play(gameState); // on joue8gamerOver = checkCheckOver(gameState); // on vérifie si la partie est finie après ce tour9}10}
avec des méthodes qui contiennent la complexité du code à côté :
Java1GameState initGameState() {2// ...3}45void play(GameState gameState) {6// ...7}89boolean checkGameOver(GameState gameState) {10// ...11}
On peut imaginer que ce jeu contienne de nombreuses données complexes, comme par exemple la liste des joueurs s'il est multijoueur, ou l'échiquier d'un jeu d'échecs.
Dans ces cas là, il est essentiel de savoir manipuler des objets. On rentre dans le vrai morceau ! YEEEHAAAA !
