从父类对象调用子类方法

26

我有以下的类

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语句显示“无法找到QuestionOption的getMultiple方法”。QuestionOption有许多子类,这些子类具有不同类型的方法,这些方法在子类之间不是通用的(getMultiple不是子类之间共有的方法)


你有什么常见的抽象概念吗?你需要找到一种方法来表示每个子类的具体细节,然后你就可以在Person中定义一个方法,在子类中覆盖它并实现适当的行为。 - Marko Topolnik
4
举例来说,你在计算什么?为什么不在每个子类中实现 calculate 方法? - Marko Topolnik
那个 calculate 函数中的 Person 参数是什么意思?如果它是计算的目标,也许应该改成 calculate(),然后使用 this 做一些操作(这将与子类中的重写方法很好地配合)。 - Thilo
@amicngh 哈哈..抱歉,我是这个论坛的新手。我似乎只能投票支持一个答案。你怎么接受一个答案呢? - user1349663
@user1349663 请阅读链接http://stackoverflow.com/faq#howtoask。它提到了如何投票和接受答案的方法。 - maaz
显示剩余3条评论
7个回答

45

注意:虽然这是可能的,但并不建议这样做,因为它会破坏继承的原因。最好的方法是重新设计应用程序结构,以便没有任何父级到子级的依赖关系。一个父级不应该需要知道它的子级或它们的能力。

然而...你应该可以这样做:

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

一个安全的方法是:

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

1
+1 对于一个好的答案。不过最好指出这是一种不好的方式,OP最好重新思考他的类层次结构。 - Keppil
1
是的,绝对不是正确的方法,但我只是想指出一个可能的选项。顺便说一下,我已经在答案中添加了警告提示。 - techfoobar
1
@Keppil 几乎所有的事情都是可以做到的,但这并不意味着通过解决他的问题我们真正地在帮助他。一个糟糕的设计会产生后果,在我看来,我们应该帮助他拥有一个清晰的设计。 - Matteo
有什么办法可以避免在calculate()方法中传递Student类吗?(我有很多类,我想让calculate()方法通用化) - humble_wolf
协议定义了一个类需要具有某些函数。一个类可以继承自父类,并且可以有一个协议来规定必须存在某些函数。似乎有一些潜在的解决方案,其中一个可能想要这样做。这可能允许以一种完全不可能的方式重用代码。 - Tony Topper
显示剩余6条评论

14

父类不应该知道子类的存在。你可以实现一个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 Teacher
    }

}

顺便提一句,你有关抽象类的陈述令人困惑。你可以调用在抽象类中定义的方法,但当然只能对子类的实例进行调用。

在你的示例中,你可以将Person定义为抽象类,然后在StudentTeacher实例上使用getName()方法。


在第二个例子中,是使用“// 处理教师”而不是“// 处理学生”吗? - BugliL

4
许多答案都建议使用“经典面向对象分解”来实现变量类型。也就是说,可能需要在其中一种变体上使用的任何内容都必须在层次结构的基础上声明。我认为这是一种类型安全但通常很糟糕的方法。你要么最终暴露所有不同变体的所有内部属性(其中大多数对于每个特定变体都是“无效”的),要么就会在层次结构的API中弄乱大量的过程性方法(这意味着每次想到新过程时都必须重新编译)。
我犹豫了一下,但这里是我写的一个博客文章的无耻宣传,概述了Java中约8种实现变体类型的方式。它们都很糟糕,因为Java在变体类型方面很差。到目前为止,唯一正确处理变体类型的JVM语言是Scala。

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

Scala的创作者实际上写了一篇关于这八种方式中的三种的论文。如果我能找到它,我会在答案中更新链接。
更新:已经找到这里

3
为什么不在Person类中写一个空方法并在子类中覆盖它呢?需要时调用它即可。
void caluculate(Person p){
  p.dotheCalculate();
}

这意味着您必须在两个子类中都使用相同的方法,但我不认为这会造成任何问题。

2

我曾经遇到过同样的情况,通过一些工程技巧找到了以下方法解决:

  1. You have to have your method in parent class without any parameter and use - -

    Class<? extends Person> cl = this.getClass(); // inside parent class
    
  2. Now, with 'cl' you can access all child class fields with their name and initialized values by using - -

    cl.getDeclaredFields(); cl.getField("myfield"); // and many more
    
  3. In this situation your 'this' pointer will reference your child class object if you are calling parent method through your child class object.

  4. Another thing you might need to use is Object obj = cl.newInstance();

如果你还是卡在某个地方,请告诉我。


1
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();


   }
   }

1
你好,欢迎来到 Stack Overflow。请尝试添加更多的注释(不是在代码中),以便解释为什么你认为这是作者正在寻找的答案。仅有代码的回答并不是很好的回答。 - marverix

0

一个可能的解决方案是

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

谢谢您的回复。我在某个地方读到,instanceof 是那些会导致很多错误的雷区之一。如果我使用它,编码时应该注意什么? - user1349663
是的,通常在一个类中有很多 instanceof 不是一个好的面向对象编程实践,但只要你没有重复的类/接口名称或者通过不同的类加载器加载它,就不会导致任何错误。 - maaz

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接