Je ne comprends pas pourquoi la méthode ab.m3 ()
appelle la fonction de la classe parent et non celle de l'enfant. J'ai pensé que passer un nouvel Integer
à la méthode pourrait appeler la méthode de la classe parente car Integer
est un Object
alors je l'ai essayé avec un int
mais cela m'a quand même donné le même résultat!
B.m1 A.m2 A.m3 A.m3
Sortie:
public class A { public void m1(){ System.out.println("A.m1"); } public void m2(){ System.out.println("A.m2"); } public void m3(Object x){ System.out.println("A.m3"); } } public class B extends A{ public void m1(){ System.out.println("B.m1"); } public void m2(int x){ System.out.println("B.m2"); } public void m3(int x){ System.out.println("B.m3"); } public static void main(String[] argv){ A aa = new A(); A ab = new B(); int num = 2; ab.m1(); ab.m2(); ab.m3(new Integer(2)); ab.m3(num); } }
3 Réponses :
Votre référence ab
est de type A
.
Lors de la compilation de ab.m3 (num);
, le compilateur ne regarde pas le type d'objet. En général, il ne saura pas toujours quel est le type d'objet. Au lieu de cela, il regarde le type de référence. Il ne peut pas correspondre à B.m3 (int)
, car le type de référence n'est pas de type B.
Le compilateur choisit donc la méthode A.m3 (Object)
, qui pourrait être surchargée lors de l'exécution. Mais ce n'est pas le cas, donc l'implémentation A est appelée.
B.m3
ne remplace pas A.m3
, car les listes de paramètres ne sont pas compatibles.
Parce que la seule méthode correspondante dans A
est A.m3
, et comme il n'y a pas de remplacement dans B
, c'est A.m3
qui sera appelé.
Ce. Fondamentalement, Java regarde toujours le type déclaré lors du choix de la méthode à appeler. Parce que toutes les méthodes en Java sont virtuelles, s'il choisit m1 ()
de A et que le type d'exécution est B, il appellera à la place m1 ()
de B car il remplace celui de A.
Cet exemple de code est particulièrement déroutant car, bien que l'intention soit de démontrer la substitution , il démontre en fait la surcharge .
Parce que B.m3 (int )
ne correspond pas à la signature de A.m3 (Object)
(voir JLS 8.4.8.1 ), la méthode n'est pas remplacée . Au lieu de cela, elle est surchargée.
Mais pourquoi l'appel de la méthode avec un argument int
ne sélectionne-t-il pas le bon? Dans cette version simplifiée du code:
B ab = new B(); ab.m3(2);
l'argument de m3
est un int
, il ressemble donc au la signature doit correspondre à B.m3 (int)
. Pourtant, comme correctement affirmé dans la question, c'est A.m3 (Object)
qui est appelé. La raison en est qu'en cas de surcharge , la sélection de la méthode est basée sur le type statique des références d'objet , et non sur le type d'exécution comme pour le remplacement.
Comme le type statique de ab
est A
, l'algorithme de sélection de méthode commence à rechercher une méthode m3
dans la classe A
. Le seul qu'il trouve est m3 (Object)
, mais à cause de l'autoboxing, l'argument peut être adapté pour correspondre à la signature, donc le code se compile.
Donc, si vous changez le code et déclarez simplement ab
comme étant de type B
:
A ab = new B(); ab.m3(2);
alors, surprise, c'est B. m3 (int)
qui est appelé.
En raison de ce potentiel de confusion profonde, cette situation doit être évitée à tout prix et de nombreux outils et fonctionnalités sont disponibles pour vous aider. L'annotation @Override
en est un exemple. De nombreux IDE ont également des décorations de gouttière pour indiquer le remplacement (comme illustré ci-dessous avec un triangle vert pour Eclipse).
A.m3 (Object)! = B.m3 (int)
, c'est-à-dire que vous ne remplacez pasm3
dansB
.Vous devriez prendre l'habitude d'annoter les méthodes qui sont remplacées par
@Override
. Ensuite, le compilateur peut indiquer si vous l'avez fait correctement (comme c'est le cas ici).Même si vous ajoutiez la surcharge
void m3 (Integer x)
dansB
, elle ne remplacerait pasA.m3 (Object)
en Java a des remplacements de type de retour covariants mais pas de remplacements de paramètres contravariants. Le meilleur conseil est d'éviter la surcharge lorsque cela est possible.