web-dev-qa-db-fra.com

Appeler une méthode de classe enfant à partir d'un objet de classe parent

J'ai les cours suivants

class Person {
    private String name;
    void getName(){...}}

class Student extends Person{
    String class;
    void getClass(){...}
}

class Teacher extends Person{
    String experience;
    void getExperience(){...}
}

Ceci est juste une version simplifiée de mon schéma actuel. Au départ, je ne connais pas le type de personne à créer. La fonction qui gère la création de ces objets prend alors l'objet général Person en tant que paramètre. 

void calculate(Person p){...}

Maintenant, je veux accéder aux méthodes des classes enfant en utilisant cet objet de classe parent. J'ai aussi besoin d'accéder de temps en temps aux méthodes de classe parentes pour que JE NE POUVAIS PAS LE FAIRE EN RÉSUMER


Je suppose que j'ai trop simplifié dans l'exemple ci-dessus, alors voici la structure actuelle. 

class Question {
  // private attributes
  :
  private QuestionOption option;
  // getters and setters for private attributes
  :
  public QuestionOption getOption(){...}
 }

 class QuestionOption{
 ....
 }
 class ChoiceQuestionOption extends QuestionOption{
 private boolean allowMultiple;
 public boolean getMultiple(){...}
 }

 class Survey{
  void renderSurvey(Question q) {
      /*
          Depending on the type of question (choice, dropdwn or other, I have to render
          the question on the UI. The class that calls this doesnt have compile time 
          knowledge of the type of question that is going to be rendered. Each question 
          type has its own rendering function. If this is for choice , I need to access 
          its functions using q. 
      */
      if(q.getOption().getMultiple())
        {...}
  }
 }

La déclaration if indique "impossible de trouver getMultiple pour QuestionOption". OuestionOption a beaucoup plus de classes enfants qui ont différents types de méthodes qui ne sont pas communes chez les enfants (getMultiple n'est pas commun chez les enfants)

17
user1349663

NOTE:Bien que cela soit possible, ce n'est pas du tout recommandé car cela détruit en quelque sorte la raison de l'héritage. Le meilleur moyen serait de restructurer la conception de votre application de manière à ce qu'il existe des dépendancesNOparent à enfant. Un parent ne devrait jamais avoir besoin de connaître ses enfants ou leurs capacités.

Cependant .. vous devriez pouvoir le faire comme:

void calculate(Person p) {
    ((Student)p).method();
}

un moyen sûr serait:

void calculate(Person p) {
    if(p instanceof Student) ((Student)p).method();
}
28
techfoobar

Une classe de parents ne devrait pas connaître les classes d'enfants. Vous pouvez implémenter une méthode calculate() et la remplacer dans chaque sous-classe:

class Person {
    String name;
    void getName(){...}
    void calculate();
}

et alors

class Student extends Person{
    String class;
    void getClass(){...}

    @Override
    void calculate() {
        // do something with a Student
    }
}

et

class Teacher extends Person{
    String experience;
    void getExperience(){...}

    @Override
    void calculate() {
        // do something with a Student
    }

}

Au fait. Votre déclaration concernant les classes abstraites est source de confusion. Vous pouvez appeler des méthodes définies dans une classe abstraite, mais bien sûr uniquement d'instances de sous-classes.

Dans votre exemple, vous pouvez créer Person abstract et utiliser getName() sur instanced of Student et Teacher.

7
Matteo

Beaucoup des réponses suggèrent de mettre en œuvre des types de variantes utilisant "Décomposition classique orientée objet". Autrement dit, tout ce qui pourrait être nécessaire sur l'une des variantes doit être déclaré à la base de la hiérarchie. Je soutiens que cette approche est digne de ce nom, mais souvent très mauvaise. Vous finissez par exposer toutes les propriétés internes de toutes les variantes (la plupart d'entre elles sont "non valides" pour chaque variante particulière) ou vous encombrez l'API de la hiérarchie avec des tonnes de méthodes procédurales une nouvelle procédure est imaginée).

J'hésite à le faire, mais voici un plug éhonté pour un article de blog que j'ai écrit qui décrit environ 8 façons de créer des types de variantes en Java. Ils craignent tous, parce que Java craint les variantes. Jusqu'à présent, le seul langage JVM qui fonctionne correctement est Scala.

http://jazzjuice.blogspot.com/2010/10/6-things-i-hate-about-Java-or-scala-is.html

Les créateurs de Scala ont en fait écrit un article sur trois des huit méthodes. Si je peux le localiser, je mettrai à jour cette réponse avec un lien.

UPDATE: l'a trouvé ici .

3
Judge Mental

Pourquoi n'écrivez-vous pas simplement une méthode vide dans Person et ne la remplacez pas dans les classes enfants? Et appelez-le, quand il le faut:

void caluculate(Person p){
  p.dotheCalculate();
}

Cela voudrait dire que vous devriez avoir la même méthode dans les deux classes, mais je ne vois pas pourquoi cela poserait un problème.

2
Zsolt János

J'ai eu la même situation et j'ai trouvé un moyen de contourner avec un peu d'ingénierie comme suit - -

  1. Vous devez avoir votre méthode dans la classe parente sans aucun paramètre et utiliser - -

    Class<? extends Person> cl = this.getClass(); // inside parent class
    
  2. Maintenant, avec 'cl', vous pouvez accéder à tous les champs de classe enfant avec leur nom et leurs valeurs initialisées en utilisant - -

    cl.getDeclaredFields(); cl.getField("myfield"); // and many more
    
  3. Dans cette situation, votre pointeur 'this' référencera votre objet de classe enfant si vous appelez une méthode parent via votre objet de classe enfant.

  4. Object obj = cl.newInstance () est également nécessaire.

Faites-moi savoir si vous êtes toujours coincé quelque part.

1
Pramod Kumar
class Car extends Vehicle {
        protected int numberOfSeats = 1;

        public int getNumberOfSeats() {
            return this.numberOfSeats;

        }

        public void  printNumberOfSeats() {
          //  return this.numberOfSeats;
            System.out.println(numberOfSeats);
        }


    } 

//Parent class

  class Vehicle {
        protected String licensePlate = null;

        public void setLicensePlate(String license) {
            this.licensePlate = license;
            System.out.println(licensePlate);
        }


   public static void main(String []args) {
       Vehicle c = new Vehicle();

      c.setLicensePlate("LASKF12341"); 

//Used downcasting to call the child method from the parent class. 
//Downcasting = It’s the casting from a superclass to a subclass.

      Vehicle d = new Car();
      ((Car) d).printNumberOfSeats();


   }
   }
0
Sneha S

Une solution possible peut être 

class Survey{
  void renderSurvey(Question q) {
  /*
      Depending on the type of question (choice, dropdwn or other, I have to render
      the question on the UI. The class that calls this doesnt have compile time 
      knowledge of the type of question that is going to be rendered. Each question 
      type has its own rendering function. If this is for choice , I need to access 
      its functions using q. 
  */
  if(q.getOption() instanceof ChoiceQuestionOption)
  {
    ChoiceQuestionOption choiceQuestion = (ChoiceQuestionOption)q.getOption();
    boolean result = choiceQuestion.getMultiple();
    //do something with result......
  }
 }
}
0
mtariq