ProgrammationDéveloppeur Backend

Expliquez comment fonctionne l'ordre de résolution des méthodes (method resolution order) lors de l'héritage en Java, et quels pièges peuvent survenir.

Réussissez les entretiens avec l'assistant IA Hintsage

Réponse.

En Java, la résolution de la surcharge (overloading) et de la redéfinition (overriding) des méthodes se fait selon certaines règles. Lors de la surcharge (overloading), le compilateur choisit la méthode la plus appropriée parmi toutes les versions au moment de la compilation, en fonction du type des arguments au moment de l'appel.

Points importants :

  • En présence de méthodes surchargées dans toutes les classes et superclasses, on cherche d'abord les correspondances les plus élevées dans la classe actuelle, puis dans les superclasses.
  • Le choix de la méthode s'effectue au moment de la compilation, et non de l'exécution.
  • Les conversions de type (widening, autoboxing, varargs) sont appliquées si nécessaire.

Exemple :

class Super { void print(Number n) { System.out.println("Super Number"); } } class Sub extends Super { void print(Integer i) { System.out.println("Sub Integer"); } } ... Sub s = new Sub(); s.print(5); // Sub Integer s.print(5.0); // Super Number

Ici, print(Integer) sera appelé pour l'argument de type Integer, et pour le type Double — la méthode du parent.

Question piège.

Si dans l'héritier, une méthode est redéfinie (override) et surchargée (overload), laquelle sera appelée en cas de polymorphisme ?

Réponse : En cas de polymorphisme, la version de la méthode est toujours choisie sur la base du type réel de l'objet pour les méthodes redéfinies (overriding). Pour les méthodes surchargées, le choix se fait au moment de la compilation sur la base du type de la référence.

class A { void test(Number n) { System.out.println("A:Number"); } } class B extends A { void test(Integer n) { System.out.println("B:Integer"); } @Override void test(Number n) { System.out.println("B:Number"); } } ... A obj = new B(); obj.test(1); // Appelera B:Number, malgré l'existence de test(Integer)

Exemples d'erreurs réelles dues à l'ignorance des subtilités du sujet.


Histoire

Un développeur a ajouté une version surchargée de la méthode dans une sous-classe, s'attendant à ce qu'elle soit appelée pour tous les héritiers. En production, il est devenu évident que l'appel via une référence de la classe parent conduisait à un choix de méthode basé sur le type de référence, et non sur l'objet. En fin de compte, la logique nécessaire n'a pas été exécutée.


Histoire

Dans un grand projet, la méthode equals(Object) a été surchargée par erreur avec la signature equals(MyClass obj), pensant qu'elle remplacerait l'equals standard. Lors de la comparaison dans des collections HashSet, l'equals par défaut (Object) était utilisé, ce qui entraînait une incohérence logique et des pertes de données.


Histoire

Lors de l'ajout d'une nouvelle version de la méthode avec varargs dans la hiérarchie des classes, il semblait que la version appropriée serait appelée pour les tableaux. Cependant, lors du passage explicite d'un tableau, le compilateur a choisi la mauvaise surcharge, ce qui a conduit à un traitement incorrect, et le bogue est resté non détecté pendant longtemps.