【问题标题】:Polymorphism and inheritance多态性和继承
【发布时间】:2013-05-21 23:24:53
【问题描述】:

在这样的情况下:

class A{
   public int x = 4;
   public void s3(){
      x = 3;
   }
   public void f(){
      x = 8;
      s3();
   }
}

class B extends A{
   public int x = 5;
   public void f(){
      x = 10;
      s3();
   }
}

A a = new B();
B b = (B) a;
a.f();
System.out.println(b.x);
System.out.println(a.x);

a.f() 调用类Bf(),然后f() 在赋值后调用s3() 函数。此时,s3() 仅在A 中定义,当它为x 赋值3 时,x 是类A 拥有的变量的副本。为什么s3() 不使用B 中声明的x?从理论上讲,B 不应该有自己的 s3() 函数的副本继承自 A 吗? (所以从B 中的A 继承的s3() 应该使用在B 中声明的x

【问题讨论】:

  • 为了将来参考,使用单个字符作为所有变量、方法和类名的名称会使您的代码难以遵循。
  • "为什么s3() 不使用B 中声明的x?"对于 B 应该覆盖 s3(),这与继承不同。

标签: java oop inheritance polymorphism


【解决方案1】:

您对继承时应该做什么有误解。 extends 是明智选择的保留字。 B 扩展 A 的意义在于 B 是具有附加属性的 A 的子集。您不应该在 B 中重新定义 x; A 应该处理x。通过在子类中重新定义x,您隐藏了超类的字段x(即使x 引用不同的变量类型也是如此)。

A a = new B();
System.out.println(a.x);  //4 makes sense, since we are of class A
B b = (B) a;
System.out.println(b.x);  //5 makes sense, since we are of class B
a.f();
System.out.println(a.x);  //3 makes sense, since a.f() calls s3(), which sets A's x to 3
System.out.println(b.x);  //10 

10 来自打印 b 的 x,通过调用 a.f() 分配给 10,然后调用 s3(),这就是第三个示例打印 3 的原因。要了解我的意思,请看这个:

  public void f()
   {
      x = 10; //sets B's x to 10
      s3();  //calls A's s3(), which sets A's x to 3.
   }

【讨论】:

  • 如果他不能在 B 中重新定义 x 那么什么是变量阴影.. 据我所知 .... public class Base { public String name = "Base"; public String getName() { 返回名称; } } 公共类 Sub 扩展 Base { public String name = "Sub"; public String getName() { 返回名称; } } 这是变量阴影
  • 我编辑了我的答案,包括对正在发生的事情的解释。
  • 绝对...实际上我只是摆脱了这个赞成反对的事情...遭受了很多不公正...:P
  • 接受您的回答吗?您说的是:“将 A 的本地 x 设置为 3”。 x 不是局部变量。我什至认为你不明白这个问题。
【解决方案2】:

因为它是一样的。您没有对象的两个副本(“实例”),只有一个。

由于您创建的是B 实例(new B()),它将使用B 中定义的方法。当然,当B 中没有定义方法时,它将使用超类方法实现。

所以,您只有一个x 属性,而s3 强制它为3。它工作正常。

【讨论】:

    【解决方案3】:

    为什么 s3() 不使用 B 中声明的 x?

    通常,父类中的方法看不到子类中的成员变量。

    我想这样做:

    B b = new B();
    b.f();
    

    至少足以重现您的部分困惑。这是 f() 在 B 中的样子:

    class B extends A{
       public int x = 5;
       public void f(){
          x = 10;
          s3();
       }
    }
    

    f() 等价于:

       public void f(){
          this.x = 10;
          this.s3();
       }
    

    所以调用 b.f() 意味着 f() 等价于:

       public void f(){
          b.x = 10;
          b.s3();
       }
    

    接下来,A 中的 s3() 方法内部会发生什么? s3() 看起来像这样:

    public void s3(){
          x = 3;
       }
    

    这相当于:

    public void s3(){
          this.x = 3;
       }
    

    'this' 是调用该方法的对象,从 f() 的最后一个示例中您可以看到它是 b。所以 s3() 等价于:

    public void s3(){
          b.x = 3;
       }
    

    所以 b.x 会被 3 覆盖...呃,没那么快!

    B 的一个实例也从 A 继承了一个 x,只是在 B 内部,B 的 x 遮蔽了来自 A 的 x。因此,B 中的 f() 方法分配到来自 B 的 x。然而,在 s3() 内部,b 从 A 获得的 x 不再被遮蔽,并且就 A 而言,只有一个 x——来自 A 的那个。换句话说,查找b.x 采用不同的路径,具体取决于该语句出现在哪个类中。

    s3() 执行后,最终结果是 b 有两个 x 有两个不同的值。在 B 中的方法内部,来自 B 的 x 将是可见的,在 A 中的内部方法中,来自 A 的 x 将是可见的。在 B 中的内部方法中,可以使用 super 获取 A 中的 x。

    理论上,B 不应该有自己的从 A 继承的 s3() 函数的副本?

    不要从副本的角度思考。考虑从类到类的指针,以及从类到查找表的指针。通常在计算机编程中,每个实例都有自己的实例变量,但方法由类中的所有实例共享。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-06-01
      • 2020-10-31
      • 1970-01-01
      • 1970-01-01
      • 2013-03-28
      • 1970-01-01
      相关资源
      最近更新 更多