-1

假设有两个类 Circle 和 Cylinder。Circle 是 Cylinder 类的超类。在 Circle 类中

getArea() 

计算 Circle 对象的面积,在 Cylinder 类中,通过定义该方法来计算 Cylinder 对象的总表面积来覆盖此方法。现在我做这样的事情: -

Circle c = new Cylinder(5.0);

我可以调用 Circle 类中定义的所有方法,但不能调用 Cylinder 类中定义的方法。但是当我调用

getArea()

调用getArea()的覆盖版本,但不是 Circle 类中的那个。

我不明白为什么调用覆盖版本而不是原始版本。

4

7 回答 7

1

Circle c意味着您所看到的c是 aCircle并且它准确地定义了您可以如何操作对象。它是一种对象的视图。现在,当您按照类型getArea()中定义的方式调用时Circle,编译器允许您编写句子,但运行时会动态推断出哪个是正确的调用方法。由于所引用的对象c是一个Cylinder然后getArea()调用它的方法。

这是多态性:你不能仅仅通过阅读来判断c.getArea()哪个是真正的方法将被调用。

于 2013-10-18T15:12:22.847 回答
1

我不明白为什么调用覆盖版本而不是原始版本。

当您扩展超类时,您确实继承了超类的属性和方法,但是如果您在子类中覆盖这些方法,则当对象属于子类时,将调用子类中的方法。

将其与日常示例联系起来:

class Developer{
    public writeCode{
         System.out.println("Supervisor will write the code here");
    }
}

class JrDeveloper extends Developer{
    @Override
    public writeCode{
         System.out.println("Jr.Developer will write code");
    }
}

class Test{

     public static void main(){
     Developer dev = new JrDeveloper();
     dev.writeCode();         // Do you really expect the Developer to write 
                              // the code or the Jr.Developer?
     }
}
于 2013-10-18T15:21:24.327 回答
0

Java 使用所谓的后期绑定。这意味着当您在实例上调用方法时,实例本身决定调用哪个方法。

由于您正在调用 的实例Cylinder,即使您使用的是其父类的引用,它也会使用该Cylinder方法。

像 C++ 这样的语言提供了通过使用关键字来执行后期或早期绑定的选项virtual,但是 java 没有。

于 2013-10-18T15:16:09.753 回答
0

请记住引用类型和对象类型之间的区别。

当你说

Circle c = new Cylinder(5.0);

引用是c类型Circle,但对象是类型Cylinder。因此,当您调用 时c.getArea(),您是从 API/接口/角度查看它Circle,但您调用getArea的是一个Cylinder对象。所以这就是你得到Cylinder' 实施的原因。

这意味着现在其余代码“只知道”对象引用是Circle. 因此,使用此代码的任何人都不必关心他是在处理 aCircle还是 a Cylinder,并且如果您添加更多继承自 的类Circle,那么他仍然不在乎。Circle一切对他来说都只是一个。

但是,当您添加getVolume()Cylinder. 引用类型c仍然是Circle,因此尝试调用c.getVolume()不会编译。当然Cylinder有那个方法,但是引用类型没有。所以它不会工作。

因此,请尽量记住引用类型和对象类型之间的区别。多态性意味着我可以通过一种引用来引用许多类的对象——有点像我可以使用录音机、Kindle、蓝光播放器等上的播放按钮并确切地知道它的作用。

这很酷。

于 2013-10-18T15:20:14.333 回答
0

调用方法的选择是在运行时根据对象的动态类型决定的。在您的情况下,这是一个圆柱体(不是圆形),因此将调用getArea()on的版本。Cylinder

这是设计使然,并且正是多态性应该如何工作。如果使用得当,这允许非常灵活和强大的行为,并赋予以可扩展方式编码的能力,而代码重复最少甚至没有。如果您很可能在创建子类时没有正确完成“is-a”关系,那么您应该没有理由要getArea()调用超类上的方法。

于 2013-10-18T15:08:14.973 回答
0

多态性就是这样:运行时的动态绑定。这意味着您的变量引用了您使用 new 创建的实例对象,但来自您的声明类型。

使用多态性,您可以“规范化”不同的对象行为或将等效对象“划分”为不同的行为。接口示例:

interface Animal {
  public void cry();
}

class Dog implements Animal{
  public void cry(){
    System.out.println("Wuff");
  }
}

class Cat implements Animal{
  public void cry(){
    System.out.println("Miau");
  }
}

Animal a1 = new Dog();
a1.cry();  // prints "Wuff"

Animal a2 = new Cat();
a2.cry();  // prints "Miau"
于 2013-10-18T16:54:04.910 回答
0

你不明白,因为你的继承权倒退了。使用继承的一个很好的理由是当你有一个在任何地方都使用的类时,但在某些情况下你想调整它,以便你有一个行为稍有不同的实例。这似乎是您想要的,因为您将 Cylinder 声明为 Circle 并使用它。

如果您从 Cylinder 开始并将其子类化以获得 Circle,那么一切都会变得更有意义。圆柱体不仅仅是一个圆圈,但一个圆圈只是一个被压扁的圆柱体。如果我们忽略这样一个事实,即我们可以使用高度为零的 Cylinder 获得相同的效果 - 并且它将有效地计算值 - 扩展 Cylinder 的 Circle 可能是 Cylinder 的一个非常好的替代品。现在,当您调用getArea.

这里有很多关于多态性的好答案。但我在实践中发现有一种强烈的趋势是让子类和超类颠倒过来。通过子类化,您不是在尝试创建分类法,而是在尝试改进现有行为(通常 - 子类化还有其他原因)。我发现最好考虑一下我提出的子类是否更好地成为超类。它通常不会,但这个过程让我的大脑有点伸展。

于 2013-10-18T16:25:52.463 回答