【问题标题】:Why parent class type reference variable having reference to child class object can't access child class's Methods为什么引用子类对象的父类类型引用变量不能访问子类的方法
【发布时间】:2013-03-01 19:57:31
【问题描述】:

既然这个对象(如标题所示)可以调用子类中被覆盖的方法,为什么它不能调用子类的其他方法? 我需要尽可能详细的答案,例如内存组织、JVM 中的内部逻辑等。

下面的代码会让你清楚地理解我的问题。

class A
{
  int x=10;
  public  A()
  {
    System.out.println("Constructor of class A called!!!");
  }

  public void sayGreetings()
  {
    System.out.println("accept hye from class A");
  }
}

class C extends A
{
  int x=30;//why this is not accessed by stated object.
  public C()
  {
    System.out.println("Constructor of Class C caled!!!");
  }
  public void sayGreetings()
  {
    System.out.println("accept hye from class C");
  }
  public void ssa()
  {
    System.out.println("Sat Sri Akal ji from class C");
  }
}

public class ParentClassTypeObject 
{
  public static void main(String[] args)
  {
    C cObj=new C();
    cObj.sayGreetings();
    cObj.ssa();
    A aCObj=new C();//this is let say stated object,main object
    aCObj.sayGreetings();/*here we invoked method will be child class's 
                         overriden method.*/
    //aCObj.ssa(); //why this line gives error

    System.out.println("x="+aCObj.x);
  }
}

【问题讨论】:

    标签: java oop polymorphism encapsulation


    【解决方案1】:

    因为你所拥有的对象的接口就是你写的时候选择的那个:

    A aCObj = new C();
    

    如果您想通过aCObj 变量访问C 属性,请将其声明为C。

    通过将其设为A,您可以稍后编写:

    aCObj = new A();
    

    因此,由于变量可以指向A 或C,编译器会限制您访问由A 类型公开的接口定义的方法。

    您仍然可以访问 C 的这些方法的定义,因为这是 OOP(多态性)的要点之一。

    【讨论】:

    • 但是您没有回答我的问题,为什么可以访问覆盖方法,但不能访问子类的其他方法。需要详细回答。当我们调用 aCObj.sayGreetings() 时,编译器如何在子类中选择覆盖的方法。为什么不调用类 A 的方法(sayGreetings()。
    【解决方案2】:

    引用变量指向同类型的对象或同类型的子集。

    请考虑 Parent 和 Child 是两个类,其中 Parent 是超类,而 Child 继承父类。下图会给你一个详细的解释。

    在上图中,父类引用变量将在子对象中搜索父类对象。它会找到它,因为它在那里。所以会给出输出。如果你在子类中有相同的方法(方法覆盖)它将执行子类覆盖的方法。

    但是对于子类引用变量,在Parent Class Object中是查不到子类对象的。所以这里,是不可能的。

    希望这能消除您的困惑。

    【讨论】:

      【解决方案3】:

      如果你编译代码你会得到编译时错误(不是运行时错误)。这背后的原因是

      A aCObj=new C();
      

      aCObj.sayGreetings();/* 编译器在编译时知道 aCobj 是类型 A 的引用。由于编译器认为 aCobj 属于 A 类型,并且 sayGreetings() 方法存在于 A 类中,因此调用此方法时不会出错 */

      aCObj.ssa(); /* 正如我上面提到的,编译器不知道运行时。在运行时 aCobj 将指向 C 类类型的对象,但是在编译时编译器只知道 aCobj 是 A 类类型,并且由于 A 类没有名为 ssa() 的方法,因此您将得到编译时错误。 */

      对象的一个​​简单规则:在编译时检查赋值运算符的左侧。运行时赋值运算符的右侧。 考虑一下这个说法:

      Parent obj =new Child(); obj.method1(); obj.method2();

      无论您想使用 Parent 类型的 obj 引用调用什么方法,这些方法都应该存在于 Parent 类中,因为在编译期间编译器将严格检查 Parent 类中是否存在这些方法,即使它可能存在于 Child 类中。

      【讨论】:

        【解决方案4】:

        编译器决定如果可以根据引用变量的类型调用方法。所以如果引用变量是A类的,则只能调用A类的方法。

        但也 编译器根据对象的实际类型而不是引用变量的类型来决定调用 WHICH 方法,开始对继承树进行自下而上的检查。(它从子类一直向上开始)

        所以在这种情况下,当你说aCObj.sayGreetings(); 时,编译器首先检查aCObj 的引用类型,即A。A 类有sayGreetings() 方法,所以没问题。但实际对象是C。所以编译器从子类(C)开始查找这个方法是否一直实现到超类(A)。方法`sayGreetings()在C类被覆盖。所以它调用C class sayGreetings() method(子类的)。

        另一方面,ssa() 方法属于 C 类,由于引用变量属于 A 类,因此当您尝试 aCObj.ssa(); 时编译器会出错

        这只是多态性。由于 A 类引用变量可以是 A 或 C 对象,编译器将访问限制为仅对作为超类 A 的方法的通用方法的访问。接下来它检查此方法是否在实际对象的类(C)。如果不是,它会向上移动到超类(A)并调用超类的方法。但是如果它被实现,它会调用子类(C)的方法

        【讨论】:

          【解决方案5】:

          对象的类型是 A 而不是 C,因此您无法访问实例变量,我认为如果将其公开,则可以。

          【讨论】:

            【解决方案6】:

            因为aCObj 被声明为A 类型,所以只能访问在类型A 中声明的方法。编译器不能保证它也是 C 类型的。

            例如

            你可能也有

            public class B extends A {
                public void sayGreetings() {
                    ...
                }
            }
            

            这没有 ssa 方法,但仍然可以分配给声明为 A 类型的对象

            【讨论】:

            • 但你没有回答我的问题,为什么覆盖方法可以访问,但子类的其他方法不能访问。
            • 但是aCObj正在访问子类的方法(重写代码在调用时执行),而不是它自己的类方法,为什么?它应该调用它自己的类方法而不是子类的重写方法
            • 因为方法是A声明的,当你执行该方法时,它总是会在底层实现上执行该方法
            • 是的,我同意 aCObj 是 A 类型,正如你所说的......所以调用 sayGreetings() 的这个对象应该调用 A 类的方法......同意??但实际上它调用了 C 类的 saygreetings() 方法。你现在得到我了吗???
            【解决方案7】:

            案例1.父类的引用变量可以指向其子类的对象。 案例 2. 指向其子类对象的父类的引用变量可以被类型转换为其子类的对象。

            情况1:父类的引用变量只能调用父类内部定义的方法,也可以调用覆盖父类方法的子类方法,但不能调用父类内部定义的方法。仅在子类中。

            情况2:父类的引用变量也可以调用其子类的方法。

            这是由于多态性原理。

            【讨论】:

              【解决方案8】:

              这里是您陈述的查询的详细解释:

              答案是“多态性”和“静态类型”的交集。因为 Java 在编译时是静态类型的,所以您可以从编译器获得某些保证,但您不得不遵守规则作为交换,否则代码将无法编译。在这里,相关保证是子类型(例如 Child)的每个实例都可以用作其超类型(例如 Parent)的实例。例如,您可以保证当您访问employee.getEmployeeDetails 或employee.name 时,方法或字段定义在可以分配给Parent 类型的变量employee 的任何非空对象上。为了保证这一点,编译器在决定您可以访问什么时只考虑该静态类型(基本上是变量引用的类型,Parent)。因此,您无法访问在对象的运行时类型 Child 上定义的任何成员。

              答案来自以下链接: Why do we assign a parent reference to the child object in Java?

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2016-03-26
                • 1970-01-01
                • 1970-01-01
                • 2023-04-05
                • 2018-12-27
                • 1970-01-01
                • 1970-01-01
                • 2021-03-01
                相关资源
                最近更新 更多