【问题标题】:OOP java Stack Overflow from overridden methodOOP java Stack Overflow 来自被覆盖的方法
【发布时间】:2018-10-16 17:42:13
【问题描述】:

这是一个初学者 OOP 问题,可能有很多答案,但我一直没能找到它们。我会尝试在这里提问,希望有人可以解释或指出我正确的方向。

以我的 MWE 为例,其中 RightTriangle 扩展 Rectangle 扩展 Shapes。方法semiPerimeter()是通过调用perimeter()实现的。

Rectangle 一切顺利,perimeter()semiPerimeter() 都可以使用。问题是RightTriangleperimeter() 方法覆盖了Rectangle perimeter()。运行测试时,我收到 StackOverflowError,因为 semiPerimeter() 调用 perimeter() 又调用 semiPerimeter()

为什么super.semiPerimeter() 调用不使用矩形超类中的perimeter()


MWE

Shapes.java

package ShapesPackage;
public abstract class Shapes {
    public abstract double perimeter();
    public double semiPerimeter() {
        return perimeter() / 2;
    }
}

Rectangle.java

package ShapesPackage;
public class Rectangle extends Shapes {
    public double perimeter() {
        return 2 * (3 + 4); //2*(length+height)
    }
}

RightTriangle.java

package ShapesPackage;
public class RightTriangle extends Rectangle {
    public double perimeter() {
        return super.semiPerimeter() + 5;  //half the rectangle perimeter plus some hypotenuse
    }
}

Test.java

package ShapesPackage;
public class Test {
    public static void main(String[] args) {
        Rectangle r = new rectangle();
        System.out.println(r.perimeter());
        System.out.println(r.semiPerimeter());
        RightTriangle t = new rightTriangle();
        System.out.println(t.perimeter());  //Fails
    }
}

【问题讨论】:

  • 它确实使用了super.semiPermiter() 方法。但是那个调用this.permiter()作为回报(它总是指向最孩子的实现)。从而开始一个无限循环。将调用更改为 Rectangle.this.permiter() 将解决问题(因为它专门调用 Rectangles 的实现)
  • 半周界法中没有基本情况。
  • 因为 rightTriangle 覆盖 perimiter() 所以 triangle.perimeter() --> super(Shapes).semiPermiator() --> triangle.perimiter() 这是递归调用,没有办法结束。另外,请遵循标准的 java 命名约定。另外,rightTriangle 扩展 (is-a) 矩形是否有意义?
  • @Sam Orozco:你的意思是 base case 吗? - 它用于递归调用!我试图避免所有递归调用。
  • @Andrew S:你是对的 - 为矩形形式调用 rightTriangle 是愚蠢的,但我被要求以这种方式进行练习。我猜创作者正在寻找一个多重扩展的例子。

标签: java oop methods


【解决方案1】:

您的方法 (t.perimeter()) 确实从 Shapes 类调用 semiPerimeter 方法,而后者又从 rightTriangle 类再次调用 perimeter,并且重复此操作。

注意new rightTriangle() 基本上意味着return perimeter() / 2 将调用perimeter 类中的perimeter 方法。

【讨论】:

  • 谢谢。这是否意味着,一旦被覆盖,方法将无法访问!? - 解决办法就是不要做这种愚蠢的事情?
  • 表示使用了对象的多态行为。即后期绑定调用Shapes.perimeter()实际上调用rightTriangle.perimeter()
【解决方案2】:

为什么super.semiPerimeter() 调用不使用矩形超类中的perimeter()

因为您在覆盖了perimeter() 方法的rightTriangle 对象上调用了它。调用perimiter() 调用rectangle 类是没有意义的,因为它是一个rightTriangle 对象。正因为如此,这两个方法不断地调用自己,导致StackOverflowError

【讨论】:

  • 谢谢。这是否意味着,一旦被覆盖,方法将无法访问!? - 解决办法就是不要做这样的蠢事?
  • @hpekristiansen 不,它仍然可以访问。当您在 Rectangle 上调用该方法时,您正在访问它。但是由于您是在 rightTriangle 对象上调用该方法,因此它将从 rightTriangle 类调用被覆盖的方法
【解决方案3】:

这是发生了什么:

您调用t.perimeter(),它调用super.semiPerimeter() -> perimeter() -> super.semiPerimeter() 等等等等等等。

听起来你已经知道了。我相信当你扩展一个类时,如果你创建一个与父类具有相同签名的方法,你会覆盖它,使任何调用都转到你的覆盖方法而不是原始方法。抽象类调用的perimeter隐含的是this.perimeter,而this又是什么?这是rightTriangle。因此,当您的抽象类看到 perimeter 时,它会看到您使用 rightTriangle 创建的那个。

【讨论】:

  • 谢谢。这是否意味着,一旦被覆盖,方法将无法访问!? - 解决办法就是不要做这种愚蠢的事情?
  • 本质上是的。公平地说,直角三角形并没有真正延伸矩形。我有点明白你的思路在哪里,但是如果你重写一个方法,你正在这样做,因为你不希望在与该对象交互时调用原始方法。这听起来很烦人,但它是设计的一部分是有原因的。另一方面,如果你想调用 rectangle.perimeter(),去吧,你会得到“原始”方法。
  • 顺便说一句,在命名对象时使用 CamelCasing,遵循标准是一个好习惯,它会让有经验的程序员少得多 :)
  • 谢谢 - 这是有道理的。只是为了澄清(并重复):“你是对的 - 将 rightTriangle 称为矩形形式是愚蠢的,但我被要求以这种方式进行练习。我猜创作者正在掌握多个延伸的例子”
  • 有道理,我知道看看多重扩展如何工作会有帮助。看起来它是成功的,你现在知道了一种不这样做的方法以及原因,我认为这是一场胜利。编程愉快!
猜你喜欢
  • 1970-01-01
  • 2021-12-07
  • 1970-01-01
  • 2013-06-07
  • 2021-08-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-12-13
相关资源
最近更新 更多