Java Challengers # 1: surcharge de méthode dans la machine virtuelle Java

Java Challengers # 1: surcharge de méthode dans la machine virtuelle Java


Bonne journée à tous.


Nous avons déjà lancé le prochain volet du cours "Développeur Java" , mais nous avons encore quelques documents que nous aimerions partager avec vous.


Bienvenue dans la série d'articles Java Challengers ! Cette série d'articles se concentre sur les fonctionnalités de programmation Java. Leur développement est votre moyen de devenir un programmeur Java hautement qualifié.


La maîtrise des techniques abordées dans cette série d'articles demande un certain effort, mais elles contribueront grandement à votre expérience quotidienne en tant que développeur java. Il est plus facile d'éviter les erreurs lorsque vous savez comment appliquer correctement les techniques de programmation Java de base et le suivi des erreurs est beaucoup plus facile lorsque vous savez exactement ce qui se passe dans votre code java.


Êtes-vous prêt à commencer à maîtriser les concepts de base de la programmation en Java? Commençons ensuite par notre premier puzzle!


élargissement-boxe-varargs


Le terme «surcharge de méthode»

À propos du terme surcharge, les développeurs sont enclins à penser qu'il s'agit de redémarrer le système, mais ce n'est pas le cas. En programmation, la surcharge de méthode signifie utiliser le même nom de méthode avec des paramètres différents.

Qu'est-ce que la surcharge de méthode?


La surcharge de méthodes est une technique de programmation qui permet à un développeur de la même classe d'utiliser le même nom pour des méthodes avec des paramètres différents. Dans ce cas, nous disons que la méthode est surchargée.


Le listing 1 montre des méthodes avec différents paramètres qui varient en nombre, type et ordre.


Liste 1. Trois options pour les méthodes de surcharge.


//   public class Calculator { void calculate(int number1, int number2) { } void calculate(int number1, int number2, int number3) { } } //   public class Calculator { void calculate(int number1, int number2) { } void calculate(double number1, double number2) { } } //   public class Calculator { void calculate(double number1, int number2) { } void calculate(int number1, double number2) { } } 

Surcharge de méthode et types primitifs


Dans le listing 1, vous avez vu les types primitifs int et double . Écartons-nous une minute et rappelons les types primitifs en Java.


Tableau 1. Types primitifs en Java


TapezGammeValeur par défautLa tailleExemples littéraux
booléenvrai ou fauxfaux1 bitvrai faux
octet-128 ... 12708 bits1, -90, -128
charCaractère Unicode ou 0 à 65 536\ u000016 bits'a', '\ u0031', '\ 201', '\ n', 4
court-32 768 ... 32 767016 bits1, 3, 720, 22 000
int-2 147 483 648 ... 2 147 483 647032 bits-2, -1, 0, 1, 9
longue-9.223.372.036.854.775.808 à 9.223.372.036.854.775.807064 bits-4000L, -900L, 10L, 700L
flotter3,40282347 x 1038, 1,40239846 x 10-450,032 bits1,67e200f, -1,57e-207f, .9f, 10,4F
double1,7976931348623157 x 10308, 4,9406564584124654 x 10-3240,064 bits1.e700d, -123457e, 37e1d

Pourquoi devrais-je utiliser la surcharge de méthode?


L'utilisation de la surcharge rend votre code plus propre et plus facile à lire, et permet également d'éviter les erreurs dans le programme.


Contrairement à la liste 1, imaginez un programme où vous aurez de nombreuses méthodes calculate() avec des noms similaires à calculate1 , calculate2 , calculate3 ... pas bon, non? La surcharge de la méthode calculate() vous permet d'utiliser le même nom et de modifier uniquement les paramètres nécessaires. Il est également très facile de trouver des méthodes surchargées, car elles sont regroupées en code.


Quelle surcharge n'est pas


N'oubliez pas que la modification d'un nom de variable n'est pas une surcharge. Le code suivant ne compile pas:


 public class Calculator { void calculate(int firstNumber, int secondNumber){} void calculate(int secondNumber, int thirdNumber){} } 

Vous ne pouvez pas non plus surcharger la méthode en modifiant la valeur de retour dans la signature de la méthode. Ce code ne compile pas non plus:


 public class Calculator { double calculate(int number1, int number2){return 0.0;} long calculate(int number1, int number2){return 0;} } 

Surcharge du constructeur


Vous pouvez surcharger le constructeur de la même manière que la méthode:


 public class Calculator { private int number1; private int number2; public Calculator(int number1) { this.number1 = number1; } public Calculator(int number1, int number2) { this.number1 = number1; this.number2 = number2; } } 

Résoudre le problème de surcharge de méthode


Êtes-vous prêt pour le premier test? Voyons!


Commencez par examiner attentivement le code suivant.


Listing 2. Le défi de la surcharge de méthode


 public class AdvancedOverloadingChallenge3 { static String x = ""; public static void main(String... doYourBest) { executeAction(1); executeAction(1.0); executeAction(Double.valueOf("5")); executeAction(1L); System.out.println(x); } static void executeAction(int ... var) {x += "a"; } static void executeAction(Integer var) {x += "b"; } static void executeAction(Object var) {x += "c"; } static void executeAction(short var) {x += "d"; } static void executeAction(float var) {x += "e"; } static void executeAction(double var) {x += "f"; } } 

Bon. Vous avez étudié le code. Quelle sera la conclusion?


  1. bégayer
  2. bfce
  3. efce
  4. aecf

La bonne réponse est donnée à la fin de l'article.


Que s'est-il passé maintenant? Comment la JVM compile des méthodes surchargées


Afin de comprendre ce qui s'est passé dans le Listing 2, vous devez savoir quelques éléments sur la façon dont la JVM compile les méthodes surchargées.


Tout d'abord, la JVM est raisonnablement paresseuse: elle fera toujours le moindre effort pour exécuter une méthode. Ainsi, lorsque vous pensez à la façon dont la JVM gère la surcharge, gardez à l'esprit trois caractéristiques importantes du compilateur:


  1. Élargissement
  2. Emballage (autoboxing et unboxing)
  3. Arguments à longueur variable (varargs)

Si vous n'avez jamais rencontré ces techniques, quelques exemples devraient vous aider à les comprendre. Notez que la JVM les exécute dans l'ordre dans lequel ils sont répertoriés.


Voici un exemple d'extension:


 int primitiveIntNumber = 5; double primitiveDoubleNumber = primitiveIntNumber ; 

Il s'agit de l'ordre d'extension des types primitifs:


Ordre d'extension des types primitifs


( Note du traducteur - Dans JLS, l' extension des primitives est décrite avec de grandes variations, par exemple, long peut être étendu en float ou double. )


Exemple d'emballage automatique:


 int primitiveIntNumber = 7; Integer wrapperIntegerNumber = primitiveIntNumber; 

Remarquez ce qui se passe dans les coulisses lors de la compilation du code:


 Integer wrapperIntegerNumber = Integer.valueOf(primitiveIntNumber); 

Voici un exemple de déballage:


 Integer wrapperIntegerNumber = 7; int primitiveIntNumber= wrapperIntegerNumber; 

Voici ce qui se passe dans les coulisses lors de la compilation de ce code:


 int primitiveIntNumber = wrapperIntegerNumber.intValue(); 

Et voici un exemple de méthode avec des arguments de longueur variable. Notez que les méthodes de longueur variable sont toujours les dernières à s'exécuter.


 execute(int... numbers){} 

Quels sont les arguments de longueur variable?


Les arguments de longueur variable ne sont qu'un tableau de valeurs données par trois points (...). Nous pouvons passer autant de nombres int à cette méthode.


Par exemple:


 execute(1,3,4,6,7,8,8,6,4,6,88...); //  ... 

Les arguments de longueur variable (varargs) sont très pratiques dans la mesure où les valeurs peuvent être transmises directement à une méthode. Si nous utilisions des tableaux, nous devions créer une instance de tableau avec des valeurs.


Extension: étude de cas


Lorsque nous transmettons le numéro 1 directement à la méthode executeAction() , la JVM l'interprète automatiquement comme un int . C'est pourquoi ce numéro ne sera pas transmis à la executeAction(short var) .


De même, si nous passons le nombre 1.0 JVM reconnaîtra automatiquement qu'elle est double.


Bien sûr, le nombre 1.0 peut également être un float , mais le type de tels littéraux est prédéfini. Par conséquent, dans le Listing 2, la executeAction(double var) est executeAction(double var) .


Lorsque nous utilisons le Double wrapper, il y a deux options: soit le nombre peut être décompressé dans un type primitif, soit il peut être développé en Object . (N'oubliez pas que chaque classe en Java étend la classe Object .) Dans ce cas, la JVM choisit une extension de type Double in Object , car elle nécessite moins d'efforts que le déballage.


Le dernier que nous passons est 1L et puisque nous avons spécifié le type, il est long .


Erreurs de surcharge courantes


À présent, vous avez probablement compris que les choses peuvent être source de confusion avec la surcharge de méthodes, alors examinons quelques problèmes que vous êtes susceptible de rencontrer.


Autoboxing avec wrappers


Java est un langage de programmation fortement typé et lorsque nous utilisons le wrapping automatique avec des wrappers, nous devons prendre en compte quelques éléments. Premièrement, le code suivant ne compile pas:


 int primitiveIntNumber = 7; Double wrapperNumber = primitiveIntNumber; 

L'autopacking ne fonctionnera qu'avec le type double car lorsque vous compilerez le code, il sera équivalent à ceci:


 Double number = Double.valueOf(primitiveIntNumber); 

Ce code sera compilé. Le premier int sera développé pour double puis compressé dans Double . Mais avec l'empaquetage automatique, il n'y a pas d'extension de type et le constructeur Double.valueof attend un double , pas un int . Dans ce cas, l'empaquetage automatique fonctionnera si nous effectuons une conversion de type explicite, par exemple:


 Double wrapperNumber = (double) primitiveIntNumber; 

N'oubliez pas que l' Integer ne peut pas être Long et Float et ne peut pas être Double . Il n'y a pas d'héritage. Chacun de ces types ( Integer , Long , Float et Double ) est un Number et un Object .


En cas de doute, n'oubliez pas que les numéros de wrapper peuvent être étendus en Number ou en Object . (Il y a beaucoup plus à dire sur les emballages, mais laissons cela pour un autre article.)


Littéraux de code


Lorsque nous ne spécifions pas le type d'un nombre littéral, la JVM calculera le type pour nous. Si nous utilisons directement le numéro 1 dans le code, la JVM le créera en tant int . Si nous essayons de passer 1 directement à une méthode qui accepte short , il ne sera pas compilé.


Par exemple:


 class Calculator { public static void main(String... args) { //      // ,   char, short, byte,  JVM    int calculate(1); } void calculate(short number) {} } 

La même règle s'applique lorsque le numéro 1.0 . Bien qu'il puisse s'agir d'un float , la JVM le considérera comme un double .


 class Calculator { public static void main(String... args) { //      // ,   float,  JVM    double calculate(1.0); } void calculate(float number) {} } 

Une autre erreur courante est l'hypothèse que Double ou tout autre wrapper est préférable pour une méthode qui obtient le double .


Le fait est que la machine virtuelle Java prend moins d'efforts pour étendre le wrapper Double dans un Object au lieu de le décompresser dans un type double primitif.


Pour résumer, lorsqu'il est utilisé directement dans le code java, 1 sera int et 1.0 sera double . L'extension est le moyen le plus simple à exécuter, puis il y a l'empaquetage ou le déballage et la dernière opération sera toujours des méthodes de longueur variable.


Comme un fait curieux. Savez-vous que le type char accepte les nombres?


 char anyChar = 127; // ,  ,    

Ce que vous devez vous rappeler de la surcharge


La surcharge est une technique très puissante pour les cas où vous avez besoin du même nom de méthode avec des paramètres différents. Il s'agit d'une technique utile car l'utilisation des bons noms facilite la lecture du code. Au lieu de dupliquer le nom de la méthode et d'ajouter de l'encombrement à votre code, vous pouvez simplement le surcharger.


Cela maintient le code propre et facile à lire, et réduit également le risque que les méthodes en double cassent une partie du système.


Ce qu'il faut garder à l'esprit: lors de la surcharge de la méthode, la machine virtuelle Java fera le moins d'efforts possible.


Voici l'ordre du chemin d'exécution le plus paresseux:


  • Le premier s'élargit.
  • La seconde est la boxe
  • Troisièmement, des arguments de longueur variable (varargs)

Points à considérer: des situations difficiles surviennent lors de la déclaration directe de nombres: 1 sera int et 1.0 sera double .


Souvenez-vous également que vous pouvez déclarer ces types explicitement en utilisant la syntaxe 1F ou 1f pour float et 1D ou 1d pour double .


Ceci conclut le rôle de la JVM dans la surcharge de méthode. Il est important de comprendre que la JVM est intrinsèquement paresseuse et suivra toujours le chemin le plus paresseux.


La réponse


La réponse à la liste 2 est l'option 3. efce.


En savoir plus sur la surcharge de méthodes en Java



Une introduction aux classes et aux objets pour les débutants absolus, y compris de petites sections sur les méthodes et la surcharge de méthodes.



Découvrez pourquoi il est important que Java soit un langage fortement typé et découvrez les types primitifs Java.



Découvrez les limites et les inconvénients de la surcharge de méthodes, ainsi que la façon de les résoudre à l'aide de types personnalisés et d'objets de paramètres.

Source: https://habr.com/ru/post/fr428307/


All Articles