Bonjour à nouveau! Lors de la dernière conférence, nous nous sommes familiarisés avec les classes et les constructeurs et avons appris à créer les nôtres. Aujourd'hui, nous examinerons de plus près une partie intégrante des cours que les méthodes. Une méthode est un ensemble de commandes qui vous permettent d'effectuer certaines opérations dans un programme. En d’autres termes, une méthode est une fonction ; quelque chose que votre classe peut faire. Dans d'autres langages de programmation, les méthodes sont souvent appelées « fonctions », mais en Java, le mot « méthode » est devenu plus populaire :) Dans la dernière leçon, si vous vous en souvenez, nous avons créé des méthodes simples pour la classe Cat afin que nos chats puissent miauler et saute :
public class Cat {
String name;
int age;
public void sayMeow() {
System.out.println("Meow!");
}
public void jump() {
System.out.println("Jumping gallop!");
}
public static void main(String[] args) {
Cat barsik = new Cat();
barsik.age = 3;
barsik.name = "Barsik";
barsik.sayMeow();
barsik.jump();
}
}
sayMeow()
et jump()
sont des méthodes de notre classe. Le résultat de leur travail est la sortie sur la console :
Мяу!
Прыг-скок!
Nos méthodes sont assez simples : elles impriment simplement du texte sur la console. Mais en Java, les méthodes ont une tâche principale : elles doivent effectuer des actions sur les données d'un objet . Modifiez la valeur des données d'un objet, transformez-le, affichez-le sur la console ou faites autre chose avec. Nos méthodes actuelles ne font rien avec les données de l'objet Cat
. Regardons un exemple plus clair :
public class Truck {
int length;
int width;
int height;
int weight;
public int getVolume() {
int volume = length * width * height;
return volume;
}
}
Par exemple, nous avons une classe qui représente un camion - Truck
. Une remorque de camion a une longueur, une largeur et une hauteur, ainsi qu'un poids (cela sera nécessaire plus tard). Dans la méthode, getVolume()
nous effectuons des calculs - nous transformons les données de notre objet en un nombre qui indique le volume (nous multiplions la longueur, la largeur et la hauteur). C'est le nombre qui sera le résultat de la méthode. Attention, dans la description de la méthode, il est écrit public int getVolume
. Cela signifie que le résultat de cette méthode doit être un nombre sous la forme int
. Nous avons calculé le résultat de la méthode et nous devons maintenant le renvoyer à notre programme qui a appelé la méthode. Pour renvoyer le résultat d'une méthode en Java, on utilise le mot-clé return
.
return volume;
Paramètres de la méthode
Les méthodes peuvent accepter des valeurs en entrée, appelées « paramètres de méthode ». Notre méthode actuellegetVolume()
dans la classe Truck
n'accepte aucun paramètre, essayons donc d'étendre l'exemple avec les camions. Créons une nouvelle classe - BridgeOfficer
. Un policier est de service sur le pont et contrôle tous les camions qui passent pour s'assurer que leur chargement ne dépasse pas la limite de poids autorisée.
public class BridgeOfficer {
int maxWeight;
public BridgeOfficer(int normalWeight) {
this.maxWeight = normalWeight;
}
public boolean checkTruck(Truck truck) {
if (truck.weight > maxWeight) {
return false;
} else {
return true;
}
}
}
La méthode checkTruck
prend un paramètre en entrée - un objet camion Truck
et détermine si l'agent autorisera ou non le camion à monter sur le pont. La logique de la méthode est assez simple : si le poids du camion dépasse le maximum autorisé, la méthode renvoie false
. Vous devrez chercher une autre route :( Si le poids est inférieur ou égal au maximum, vous pouvez passer et la méthode renvoie true
. Si vous ne comprenez toujours pas bien les expressions « retour », « la méthode renvoie une valeur » - faisons une pause dans la programmation et regardons cela à l'aide d'un exemple simple tiré de la vie réelle :) Disons que vous êtes tombé malade et que vous n'êtes pas au travail pendant plusieurs jours. Vous vous présentez à la comptabilité avec votre arrêt maladie, que vous devez payer. Si nous faisons une analogie avec les méthodes, alors le comptable dispose d'une méthode paySickLeave()
(« payer les congés de maladie »). Vous transmettez en paramètre une attestation d'arrêt maladie à cette méthode (sans elle, la méthode ne fonctionnera pas et vous ne serez rien payé !). Dans la méthode de la feuille de calcul, les calculs nécessaires sont effectués (le comptable l'utilise pour calculer combien l'entreprise devrait vous payer) et le résultat du travail vous est restitué - une somme d'argent. Le programme fonctionne de la même manière. Il appelle une méthode, y transmet des données et reçoit enfin le résultat. Voici la méthode main()
de notre programme BridgeOfficer
:
public static void main(String[] args) {
Truck first = new Truck();
first.weight = 10000;
Truck second = new Truck();
second.weight = 20000;
BridgeOfficer officer = new BridgeOfficer(15000);
System.out.println("Truck number 1! May I pass, officer?");
boolean canFirstTruckGo = officer.checkTruck(first);
System.out.println(canFirstTruckGo);
System.out.println();
System.out.println("Truck number 2! May I?");
boolean canSecondTruckGo = officer.checkTruck(second);
System.out.println(canSecondTruckGo);
}
Nous créons deux camions avec des charges de 10 000 et 20 000. Dans le même temps, le poids maximum pour le pont où l'officier est en service est de 15 000. Le programme a appelé la méthode officer.checkTruck(first)
, la méthode a tout calculé et a renvoyé le résultat au programme - true
, et le programme l'a enregistré dans la variable boolean canFirstTruckGo
. Désormais, il peut en faire ce qu'il veut (tout comme vous avec l'argent que vous avez reçu du comptable). Finalement le code
boolean canFirstTruckGo = officer.checkTruck(first);
revient à
boolean canFirstTruckGo = true;
Un point très important : l'opérateur return
non seulement renvoie le résultat de la méthode, mais termine également son travail ! Tout le code écrit après le retour ne sera pas exécuté !
public boolean checkTruck(Truck truck) {
if (truck.weight > maxWeight) {
return false;
System.out.println("Turn around, overweight!");
} else {
return true;
System.out.println("Alright, move on!");
}
}
Les phrases prononcées par l'agent ne seront pas transmises à la console, car la méthode a déjà renvoyé un résultat et terminé son travail ! Le programme est revenu au point où la méthode a été appelée. Vous n'avez pas à vous en soucier vous-même - le compilateur Java est suffisamment intelligent pour générer une erreur si vous essayez d'écrire du code après return
.
Avengers : guerre des options
Il existe des situations où notre programme nécessite plusieurs options pour le fonctionnement d'une méthode. Pourquoi ne créons-nous pas notre propre intelligence artificielle ? Amazon a Alexa, Yandex a Alice, alors pourquoi sommes-nous pires ? :) Dans le film sur Iron Man, Tony Stark a créé sa propre intelligence artificielle exceptionnelle - JARVIS Rendons hommage à ce merveilleux personnage et nommons notre IA en son honneur :) La première chose que nous devons apprendre à Jarvis est de saluer les gens qui entrent dans la pièce (il serait étrange qu'un si grand intellect se révèle impoli).public class Jarvis {
public void sayHi(String name) {
System.out.println("Good evening, " + name + ", How are you doing?");
}
public static void main(String[] args) {
Jarvis jarvis = new Jarvis();
jarvis.sayHi("Tony Stark");
}
}
Sortie de la console :
Добрый вечер, Тони Старк, How ваши дела?
Super! Jarvis sait comment saluer quelqu'un qui entre. Le plus souvent, bien sûr, ce sera son propriétaire, Tony Stark. Mais il ne viendra peut-être pas seul ! Et notre méthode sayHi()
ne prend qu’un seul argument en entrée. Et, par conséquent, il ne pourra saluer qu'un seul de ceux qui viendront et ignorera l'autre. Pas très poli, d’accord ? :/ Dans ce cas, pour résoudre le problème, on peut simplement écrire 2 méthodes dans la classe du même nom, mais avec des paramètres différents :
public class Jarvis {
public void sayHi(String firstGuest) {
System.out.println("Good evening, " + firstGuest + ", How are you doing?");
}
public void sayHi(String firstGuest, String secondGuest) {
System.out.println("Good evening, " + firstGuest + ", " + secondGuest + ", How are you doing?");
}
}
C'est ce qu'on appelle la surcharge de méthode . La surcharge permet à notre programme d'être plus flexible et de s'adapter à différentes options de travail. Voyons comment cela fonctionne :
public class Jarvis {
public void sayHi(String firstGuest) {
System.out.println("Good evening, " + firstGuest + ", How are you doing?");
}
public void sayHi(String firstGuest, String secondGuest) {
System.out.println("Good evening, " + firstGuest + ", " + secondGuest + ", How are you doing?");
}
public static void main(String[] args) {
Jarvis jarvis = new Jarvis();
jarvis.sayHi("Tony Stark");
jarvis.sayHi("Tony Stark", "Captain America");
}
}
Sortie de la console :
Добрый вечер, Тони Старк, How ваши дела?
Добрый вечер, Тони Старк, Капитан Америка, How ваши дела?
Super, les deux options ont fonctionné :) Cependant, nous n'avons pas résolu le problème ! Et s'il y a trois invités ? Bien entendu, nous pouvons à nouveau surcharger la méthode sayHi()
pour accepter les noms de trois invités. Mais il peut y en avoir 4 ou 5. Et ainsi de suite à l’infini. Existe-t-il une autre façon d'apprendre à Jarvis à travailler avec un nombre illimité de noms, sans un million de surcharges de méthodes sayHi()
? :/ Bien sûr ! Sinon, Java serait-il le langage de programmation le plus populaire au monde ? ;)
public void sayHi(String...names) {
for (String name: names) {
System.out.println("Good evening, " + name + ", How are you doing?");
}
}
Le record( String...names
) passé en paramètre permet d'indiquer qu'un certain nombre de chaînes sont passées à la méthode. Nous ne précisons pas à l’avance combien il doit y en avoir, le fonctionnement de notre méthode devient donc désormais beaucoup plus flexible :
public class Jarvis {
public void sayHi(String...names) {
for (String name: names) {
System.out.println("Good evening, " + name + ", How are you doing?");
}
}
public static void main(String[] args) {
Jarvis jarvis = new Jarvis();
jarvis.sayHi("Tony Stark", "Captain America", "Black Widow", "Hulk");
}
}
Sortie de la console :
Добрый вечер, Тони Старк, How ваши дела?
Добрый вечер, Капитан Америка, How ваши дела?
Добрый вечер, Черная Вдова, How ваши дела?
Добрый вечер, Халк, How ваши дела?
Une partie du code ici ne vous est pas familière, mais cela ne vous dérange pas. Son essence est simple : la méthode parcourt tour à tour tous les noms et salue chacun des invités ! De plus, cela fonctionnera pour n’importe quel nombre de lignes transférées ! Deux, dix, voire mille : la méthode fonctionnera de manière fiable quel que soit le nombre d'invités. Bien plus pratique que de faire des surcharges pour toutes les options possibles, n'est-ce pas ? :) Autre point important : l'ordre des arguments compte ! Disons que notre méthode prend une chaîne et un nombre en entrée :
public class Man {
public static void sayYourAge(String greeting, int age) {
System.out.println(greeting + " " + age);
}
public static void main(String[] args) {
sayYourAge("My age - ", 33);
sayYourAge(33, "My age - "); //error!
}
}
Si une méthode sayYourAge
de classe Man
prend une chaîne et un nombre en entrée, alors c'est l'ordre dans lequel ils doivent être transmis dans le programme ! Si nous les transmettons dans un ordre différent, le compilateur générera une erreur et la personne ne pourra pas connaître son âge. D'ailleurs, les constructeurs que nous avons abordés dans le dernier cours sont aussi des méthodes ! Ils peuvent également être surchargés (créer plusieurs constructeurs avec différents ensembles d'arguments) et pour eux, l'ordre de passage des arguments est également fondamentalement important. De vraies méthodes ! :)
Et encore sur les paramètres
Oui, oui, nous n'en avons pas encore fini avec eux :) Le sujet que nous allons aborder maintenant est très important. Il y a 90 % de chances qu’ils vous posent cette question lors de tous vos futurs entretiens ! Nous parlerons de la transmission de paramètres aux méthodes. Regardons un exemple simple :public class TimeMachine {
public void goToFuture(int currentYear) {
currentYear = currentYear+10;
}
public void goToPast(int currentYear) {
currentYear = currentYear-10;
}
public static void main(String[] args) {
TimeMachine timeMachine = new TimeMachine();
int currentYear = 2020;
System.out.println("What is the year now?");
System.out.println(currentYear);
timeMachine.goToPast(currentYear);
System.out.println("And now?");
System.out.println(currentYear);
}
}
La machine à voyager dans le temps dispose de deux méthodes. Les deux prennent en entrée un nombre représentant l'année en cours et incrémentent ou décrémentent la valeur (selon que l'on souhaite remonter dans le temps ou dans le futur). Mais, comme le montre la sortie de la console, la méthode n’a pas fonctionné ! Sortie de la console :
Какой сейчас год?
2020
А сейчас?
2020
Nous avons passé une variable currentYear
à la méthode goToPast()
, mais sa valeur n'a pas changé. Comme c’était le cas en 2020, cela reste ainsi. Mais pourquoi? :/ Parce que les primitives en Java sont transmises aux méthodes par valeur. Qu'est-ce que ça veut dire? Lorsque nous appelons une méthode goToPast()
et y passons notre variable int currentYear = 2020
, ce n'est pas la variable elle-même qui entre dans la méthode currentYear
, mais une copie de celle-ci . La valeur de cette copie, bien sûr, est également égale à 2020, mais toutes les modifications apportées à la copie n'affectent en rien notre variable d'originecurrentYear
! Rendons notre code plus verbeux et voyons ce qui se passe aveccurrentYear
:
public class TimeMachine {
public void goToFuture(int currentYear) {
currentYear = currentYear+10;
}
public void goToPast(int currentYear) {
System.out.println("The goToPast method has started!");
System.out.println("The currentYear value inside the goToPast method (at the beginning) = " + currentYear);
currentYear = currentYear-10;
System.out.println("The currentYear value inside the goToPast method (at the end) = " + currentYear);
}
public static void main(String[] args) {
TimeMachine timeMachine = new TimeMachine();
int currentYear = 2020;
System.out.println("What is the year at the very beginning of the program?");
System.out.println(currentYear);
timeMachine.goToPast(currentYear);
System.out.println("What year is it now?");
System.out.println(currentYear);
}
}
Sortie de la console :
Какой год в самом начале работы программы?
2020
Метод goToPast начал работу!
Значение currentYear внутри метода goToPast (в начале) = 2020
Значение currentYear внутри метода goToPast (в конце) = 2010
А сейчас Howой год?
2020
Cela montre clairement que la variable transmise à la méthode goToPast()
n'est qu'une copie currentYear
. Et changer la copie n’avait aucun effet sur la signification de « l’original ». " Passer par référence " a exactement le sens opposé. Pratiquons sur les chats ! Je veux dire, voyons à quoi ressemble le passage par lien en utilisant les chats comme exemple :)
public class Cat {
int age;
public Cat(int age) {
this.age = age;
}
}
Maintenant, avec l'aide de notre machine à remonter le temps, nous allons lancer Barsik, le premier chat voyageur temporel au monde, dans le passé et le futur ! Changeons la classe TimeMachine
pour que la machine puisse travailler avec des objets Cat
;
public class TimeMachine {
public void goToFuture(Cat cat) {
cat.age += 10;
}
public void goToPast(Cat cat) {
cat.age -= 10;
}
}
Les méthodes modifient désormais non seulement le numéro transmis, mais également le champ age
d'un objet spécifique Cat
. Dans le cas des primitives, comme vous vous en souvenez, nous n’avons pas réussi : le numéro d’origine n’a pas changé. Voyons ce qui se passe ici !
public static void main(String[] args) {
TimeMachine timeMachine = new TimeMachine();
Cat barsik = new Cat(5);
System.out.println("How old is Barsik at the very beginning of the program?");
System.out.println(barsik.age);
timeMachine.goToFuture(barsik);
System.out.println("And now?");
System.out.println(barsik.age);
System.out.println("Firs-sticks! Barsik has aged 10 years! Drive back quickly!");
timeMachine.goToPast(barsik);
System.out.println("Did it work? Have we returned the cat to its original age?");
System.out.println(barsik.age);
}
Sortie de la console :
Сколько лет Барсику в самом начале работы программы?
5
А теперь?
15
Елки-палки! Барсик постарел на 10 лет! Живо гони назад!
Получилось? Мы вернули коту его изначальный возраст?
5
Ouah! Maintenant, la méthode a fonctionné différemment : notre chat a soudainement vieilli, puis a paru à nouveau plus jeune ! :) Essayons de comprendre pourquoi. Contrairement à l'exemple avec les primitives, dans le cas des objets, une référence à l'objet est transmise à la méthode. Une référence à notre objet d'origine a été transmise goToFuture(barsik)
aux méthodes . Par conséquent, lorsque nous changeons de méthode interne , nous accédons à la zone mémoire même où notre objet est stocké. Ceci est un lien vers le même Barsik que nous avons créé au tout début. C'est ce qu'on appelle le "passage par référence" ! Cependant, avec ces liens tout n'est pas si simple :) Essayons de changer notre exemple : goToPast(barsik)
barsik
barsik.age
public class TimeMachine {
public void goToFuture(Cat cat) {
cat = new Cat(cat.age);
cat.age += 10;
}
public void goToPast(Cat cat) {
cat = new Cat(cat.age);
cat.age -= 10;
}
public static void main(String[] args) {
TimeMachine timeMachine = new TimeMachine();
Cat barsik = new Cat(5);
System.out.println("How old is Barsik at the very beginning of the program?");
System.out.println(barsik.age);
timeMachine.goToFuture(barsik);
System.out.println("Barsik went to the future! Has his age changed?");
System.out.println(barsik.age);
System.out.println("And if you try in the past?");
timeMachine.goToPast(barsik);
System.out.println(barsik.age);
}
}
Sortie de la console :
Сколько лет Барсику в самом начале работы программы?
5
Барсик отправился в будущее! Его возраст изменился?
5
А если попробовать в прошлое?
5
Ça ne marche plus ! O_O Voyons ce qui s'est passé :) Tout dépend des méthodes goToPast
/ goToFuture
et des mécanismes de fonctionnement des liens. Maintenant attention !Ce point est le plus important pour comprendre le fonctionnement des liens et des méthodes. En fait, lorsque l'on appelle une méthode, goToFuture(Cat cat)
ce n'est pas la référence de l'objet lui-même qui lui est transmise cat
, mais une copie de cette référence. Autrement dit, lorsque nous passons un objet à une méthode, il y a deux références à cet objet . C’est très important pour comprendre ce qui se passe. Après tout, c’est pourquoi notre dernier exemple n’a pas modifié l’âge du chat. Dans l'exemple précédent avec changement d'âge, nous avons simplement pris la référence passée dans la méthode goToFuture()
, trouvé l'objet en mémoire en l'utilisant et modifié son âge ( cat.age += 10
). Maintenant, dans la méthode goToFuture()
, nous créons un nouvel objet
(cat = new Cat(cat.age)),
et le même lien de copie qui a été transmis à la méthode se voit attribuer cet objet. Par conséquent:
- Le premier lien (
Cat barsik = new Cat(5)
) pointe vers le chat d'origine (âgé de 5 ans) - Après avoir transmis la variable
cat
à la méthodegoToPast(Cat cat)
et l'avoir affectée à un nouvel objet, la référence a été copiée.
cat.age += 10;
Et naturellement, lorsque nous le sortons main()
sur la console dans la méthode barsik.age
, nous constatons que son âge n'a pas changé. Après tout barsik
, il s'agit d'une variable de référence qui pointe toujours vers l'ancien objet original âgé de 5 ans, auquel rien n'est arrivé. Toutes nos manipulations avec l'âge ont été réalisées sur un objet neuf. Ainsi, il s'avère que les objets sont transmis aux méthodes par référence. Les copies d'objets ne sont jamais créées automatiquement. Si vous avez transmis un objet cat à une méthode et modifié son âge, il changera avec succès. Mais les valeurs des variables de référence sont copiées lors de l'affectation et/ou de l'appel de méthodes ! Répétons ici le paragraphe sur le passage de primitives : "Lorsque nous appelons une méthode changeInt()
et y passons notre variable int x = 15
, ce n'est pas la variable elle-même qui entre dans la méthode x
, mais sa copie . Après tout, tous les changements qui arrivent à la copie ne sont pas pris en compte. affecter notre variable d'origine de quelque manière que ce soit x
. Avec la copie des liens, tout fonctionne exactement de la même manière ! Vous transmettez l'objet cat à la méthode. Si vous faites quelque chose avec le chat lui-même (c'est-à-dire avec l'objet en mémoire), tous les changements seront effectués avec succès - nous n'avions qu'un seul objet et nous l'avons toujours. Mais si à l’intérieur d’une méthode vous créez un nouvel objet et l’enregistrez dans une variable de référence, qui est un paramètre de la méthode, nous avons désormais deux objets et deux variables de référence. C'est tout! Ce n'était pas si simple, il fallait même faire plusieurs cours. Mais l’essentiel est que vous ayez appris ce sujet extrêmement important. Vous rencontrerez souvent des disputes (même parmi les développeurs expérimentés) sur la façon dont les arguments sont transmis en Java. Vous savez maintenant exactement comment cela fonctionne. Continuez comme ça! :)
GO TO FULL VERSION