Вызов метода дочернего класса из объекта родительского класса

У меня есть следующие классы

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

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

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

Это просто упрощенная версия моей реальной схемы. Первоначально я не знаю тип человека, который должен быть создан, поэтому функция, которая обрабатывает создание этих объектов, принимает в качестве параметра общий объект Person.

void calculate(Person p){...}

Теперь я хочу получить доступ к методам дочерних классов, используя этот объект родительского класса. Мне также нужно время от времени обращаться к методам родительского класса, поэтому Я НЕ МОГУ СДЕЛАТЬ ЭТО РЕЗЮМЕ.


Я думаю, я упростил слишком много в приведенном выше примере, так что здесь идет речь, это фактическая структура.

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())
        {...}
  }
 }

Утверждение if говорит, что "не может найти getMultiple для QuestionOption". В OuestionOption есть еще много дочерних классов, которые имеют разные типы методов, которые не являются общими для детей (getMultiple не распространен среди детей)

Ответы

Ответ 1

ПРИМЕЧАНИЕ.. Хотя это возможно, оно совсем не рекомендуется, поскольку оно разрушает причину наследования. Лучшим способом было бы реструктурировать дизайн вашего приложения, чтобы родительские зависимости НЕТ. Родительу никогда не нужно знать своих детей или их возможности.

Однако.. вы должны иметь возможность сделать это, как:

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

безопасный способ:

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

Ответ 2

Родительский класс не должен знать о дочерних классах. Вы можете реализовать метод calculate() и переопределить его в каждом подклассе:

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

а затем

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

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

и

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

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

}

Кстати. Ваше утверждение об абстрактных классах сбивает с толку. Вы можете вызывать методы, определенные в абстрактном классе, но, конечно, только экземпляры подклассов.

В вашем примере вы можете сделать реферат Person и использовать getName() по умолчанию Student и Teacher.

Ответ 3

Многие из ответов здесь предлагают варианты вариантов реализации, используя "Классическое объектно-ориентированное разложение". То есть все, что может потребоваться в одном из вариантов, должно быть объявлено в базе иерархии. Я утверждаю, что это безопасный по типу, но часто очень плохой подход. Вы либо полностью раскрываете все внутренние свойства всех разных вариантов (большинство из которых являются "недействительными" для каждого конкретного варианта), либо вы загромождаете API иерархии множеством процедурных методов (это означает, что вам приходится перекомпилировать каждый раз новая процедура мечтает).

Я смущаюсь сделать это, но вот бесстыдный плагин для сообщения в блоге, который я написал, который описывает около 8 способов использования вариантов вариантов в Java. Они все сосут, потому что Java отстой в вариантах. Пока единственным языком JVM, который правильно его использует, является Scala.

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

Создатели Scala действительно написали статью о трех из восьми способов. Если я могу отследить его, я обновлю этот ответ ссылкой.

UPDATE: нашел здесь.

Ответ 4

Почему бы вам просто не написать пустой метод в Person и переопределить его в дочерних классах? И назовите это, когда это должно быть:

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

Это означает, что вы должны иметь один и тот же метод в обоих дочерних классах, но я не понимаю, почему это было бы проблемой вообще.

Ответ 5

У меня была такая же ситуация, и я нашел способ с немного инженерии следующим образом: -

  • Вы должны иметь свой метод в родительском классе без каких-либо параметров и использовать - -

    Class<? extends Person> cl = this.getClass(); // inside parent class
    
  • Теперь, используя 'cl', вы можете получить доступ ко всем дочерним классам с их именем и инициализированными значениями, используя - -

    cl.getDeclaredFields(); cl.getField("myfield"); // and many more
    
  • В этой ситуации ваш указатель 'this' будет ссылаться на ваш дочерний объект класса, если вы вызываете родительский метод через объект дочернего класса.

  • Еще одна вещь, которую вам, возможно, потребуется использовать, - Object obj = cl.newInstance();

Сообщите мне, если еще что-то застряло.

Ответ 6

Одним из возможных решений может быть

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......
  }
 }
}