27 votes

Une méthode de la superclasse est appelée alors que l'objet appartient à une sous-classe.

Je jouais avec des règles de surcharge simples et j'ai trouvé quelque chose d'intéressant. Voici mon code.

package com.demo;

public class Animal {

    private void eat() {
        System.out.println("animal eating");
    }

    public static void main(String args[]) {

        Animal a = new Horse();
        a.eat();
    }
}

class Horse extends Animal {
    public void eat() {
        System.out.println("Horse eating");
    }
}

Ce programme produit les résultats suivants.

alimentation animale

Voici ce que je sais :

  • Comme nous l'avons private void eat() il n'est pas certain que l'on y accède dans une sous-classe, de sorte que la question du remplacement de la méthode ne se pose pas ici, car la méthode JLS le définit clairement.
  • Maintenant qu'il ne s'agit pas d'une méthode prioritaire, elle ne va certainement pas appeler public void eat() de la classe Cheval
  • Maintenant, notre déclaration Animal a = new Horse(); est valide en raison du polymorphisme.

Pourquoi est-ce que a.eat() invoquer une méthode à partir du Animal classe ? Nous créons une Horse alors pourquoi la méthode de la classe Animal est-elle appelée ?

24voto

templatetypedef Points 129554

Méthodes marquées private ne peuvent pas être surchargés dans les sous-classes car ils ne sont pas visibles pour la sous-classe. En un sens, votre Horse n'a pas la moindre idée que Animal a un eat puisqu'il est marqué private . Par conséquent, Java ne tient pas compte de l'élément Horse 's eat pour être une surcharge. Il s'agit principalement d'une mesure de sécurité. Si une classe dispose d'une méthode, elle porte la mention private En effet, on part du principe que cette méthode est censée être utilisée uniquement pour les internes de la classe et qu'elle est totalement inaccessible au monde extérieur. Si une sous-classe peut surcharger une méthode private il pourrait alors potentiellement modifier le comportement d'une superclasse de manière inattendue, ce qui est (1) inattendu et (2) un risque potentiel pour la sécurité.

Parce que Java suppose qu'un private d'une classe ne sera pas surchargée, lorsque vous appelez une méthode private par l'intermédiaire d'une référence d'un certain type, Java utilisera toujours le type de la méthode référence pour déterminer la méthode à appeler, plutôt que d'utiliser le type de l'élément d'information. l'objet pointé par cette référence pour déterminer la méthode à appeler. Ici, la référence est de type Animal C'est donc cette méthode qui est appelée, même si cette référence pointe vers un objet de type Horse .

14voto

Adrian Shum Points 10784

Je ne suis pas sûr de comprendre votre confusion. Sur la base de ce que vous savez :

Vous avez raison, Horse.eat() ne remplace pas Animal.eat() (car il est privé). En d'autres termes, lorsque vous appelez anAnimal.eat() il n'y a pas de liaison tardive et donc, vous appelez simplement Animal.eat() et c'est ce que vous voyez.


D'après votre autre commentaire, il semble que votre confusion porte sur la façon dont le compilateur décide de ce qu'il faut appeler. Voici une explication de très haut niveau :

Quand le compilateur voit Animal a =...; a.eat(); il essaiera de déterminer ce qu'il faut appeler.

Par exemple, s'il voit eat() est une méthode statique, étant donné a est une référence à Animal le compilateur le traduira en appelant Animal.eat() .

S'il s'agit d'une méthode d'instance, et qu'elle rencontre une méthode qui peut avoir été surchargée par une classe enfant, le compilateur ne génère pas d'instructions pour appeler une méthode spécifique. Au lieu de cela, il va générer des instructions pour faire une sorte de recherche à partir d'une vtable. Conceptuellement, chaque objet aura une petite table, dont la clé est la signature de la méthode, et la valeur est la référence à la méthode réelle à appeler. Par exemple, si dans votre cas, Animal.eat() n'est pas privée, ce que la vtable de Horse contiendra est quelque chose comme ["eat()" -> "Horse.eat()"] . Donc à temps de fonctionnement , étant donné une Animal référence et eat() est appelé, ce qui se passe est en fait : la recherche de la vtable de l'objet référencé avec eat() et appeler la méthode associée. (Si la référence pointe sur un Horse la méthode associée sera Horse.eat() ). C'est ainsi que la magie de la reliure tardive s'opère dans la plupart des cas.

Avec une méthode d'instance qui ne peut pas être surchargée, les compilateurs font la même chose que pour les méthodes statiques et génèrent des instructions pour appeler cette méthode directement.

(Ce qui précède n'est pas techniquement exact, c'est juste une illustration conceptuelle pour vous permettre de comprendre ce qui s'est passé).

8voto

GhostCat Points 83269

Ce que vous oubliez probablement ici, c'est que votre méthode principale se trouve dans la classe Animal. Il n'y a donc aucun problème à appeler la méthode privée eat() depuis la même classe. Si vous déplacez votre méthode principale dans une autre classe, vous constaterez que l'appel de la méthode eat() sur un objet de type Animal entraînera alors une erreur de compilation !

Et bien sûr : si vous aviez mis l'annotation @Override sur eat() dans Horse, vous auriez également reçu une erreur de compilation. Car, comme d'autres l'ont joliment expliqué, vous ne surchargez rien dans votre exemple.

Donc, en substance :

  1. Vous n'avez pas remplacé quoi que ce soit
  2. Vous n'appeliez pas la méthode que vous pensiez appeler

Enfin, en ce qui concerne votre commentaire : bien sûr qu'il y a une Animal objet. Le cheval est extension de Animal ; ainsi tout objet Cheval est aussi un objet Animal. C'est pourquoi vous avez pu écrire

Animal a = new Horse();

Mais la chose importante à comprendre : après cette ligne, le compilateur ne sait plus que "a" est en fait un cheval. Vous avez déclaré "a" en tant qu'animal ; et par conséquent, le compilateur vous permet d'appeler les méthodes déclarées par l'animal. N'oubliez pas : héritage consiste essentiellement à décrire une relation "IS-A" : dans votre exemple, un cheval EST UN animal.

3voto

Kedar Mhaswade Points 332

En bref, vous surchargez la signification prévue du terme "overriding" en Java :-).

Imaginons que quelqu'un d'autre ait écrit le Animal (en la réécrivant légèrement, sans changer la sémantique, mais pour démontrer une bonne pratique). Nous supposerons également que Animal compile et fonctionne bien :

public class Animal {

    public static void main(String args[]) {

        Animal a = new Animal(); // yes, Animal, no Horse yet.
        a.eat();
    }
    ///// Animal's private methods, you should not look here
    private void eat() {
        System.out.println("animal eating");
    }
    ///// Animal's private methods, you should not look here
}

Il s'agit d'une bonne pratique de codage Java, car l'auteur de l'article Animal ne veut pas que vous, le lecteur de ce code, sachiez vraiment quoi que ce soit sur Animal de l'entreprise privée.

Ensuite, vous regardez le public static void main méthode de Animal et déduire correctement qu'il existe une méthode nommée eat() qui y sont définis. A ce stade, ce qui suit est valable :

  1. Comme toute autre classe en Java, Animal étend Object .
  2. Vous regardez les méthodes publiques (et protégées) de l'application Object et découvrir qu'il n'existe pas de méthode telle que eat() . Étant donné que Animal compile bien, vous pouvez en déduire que eat doivent être Animal Les affaires privées de l'UE ! Il n'y a pas d'autre moyen pour que Animal pourrait compiler. Ainsi, sans regarder Animal Vous pouvez en déduire qu'il y a un lien entre l'entreprise privée et l'entreprise privée. eat() méthode dans Animal classe qui est privé !

Disons maintenant que votre intention était de créer un autre animal nommé Horse en tant que spécialiste Animal et lui donner un comportement spécial de manger. Vous vous dites que vous êtes pas va regarder dans Java Lang Spec et trouver toutes les règles pour le faire et juste utiliser la extends mot-clé et en finir avec lui. La première version de Horse puis émerge. Vous avez cependant entendu quelque part qu'il est préférable de clarifier votre intention de Remplacement de (c'est une chose dont vous êtes maintenant certain -- vous voulez vraiment contourner eat comportement des Horse ) :

class Horse extends Animal {
    @Override
    public void eat() {
        System.out.println("Horse eating");
    }
}

Exact ; vous ajoutez la balise @Override . C'est toujours une bonne idée, certes, à un verbiage accru (C'est une bonne pratique pour quelques raisons que nous ne détaillerons pas ici).

Vous essayez de compiler Horse.java et vous voyez :

Error:(21, 5) java: method does not override or implement a 
method from a supertype

Ainsi, le compilateur qui connaît le langage de programmation Java mieux que nous, nous dit que nous sommes, en fait, pas en passant outre ou mise en œuvre de une méthode qui est déclarée dans un supertype .

Maintenant, la manipulation des surcharges en Java devient plus claire pour nous. Puisque nous sommes censés ne remplacer que les comportements conçus pour être remplacés, à savoir les méthodes publiques et protégées, nous devons faire attention à la façon dont les superclasses sont écrites. Dans ce cas, par inadvertance, la superclasse Animal qui a été apparemment conçu pour l'extension, rendait impossible pour les sous-classes de remplacer l'option de l'utilisateur. eat comportement !

Même si nous avons retiré le @Override En effet, comme vous l'avez observé, au moment de l'exécution, la méthode involontaire dont la signature correspond est appelée. C'est encore pire.

0voto

Shankar Shastri Points 737

Les méthodes privées ne peuvent pas être surchargées.

http://ideone.com/kvyngL

/* package whatever; // don't place package name! */

import java.util.*;
import java.lang.*;
import java.io.*;

class Animal {

    private void eat() {
        System.out.println("animal eating");
    }
}

class Horse extends Animal {
    public void eat() {
        System.out.println("Horse eating");
    }
}
/* Name of the class has to be "Main" only if the class is public. */
class Ideone
{
    public static void main (String[] args) throws java.lang.Exception
    {
        // your code goes here
                Animal a = new Horse();
        a.eat();

    }
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X