【问题标题】:Square and Rectangle Inheritance正方形和矩形继承
【发布时间】:2014-06-14 17:31:35
【问题描述】:

这是一个关于 Java 中具有两个类的基本继承的问题。

我们有两个类,第一个是Rectangle

private double length;
private double width;

public Rectangle(double length, double width)
{
    this.length = length;
    this.width = width;
}

接下来我们有一个名为Square的扩展类,它扩展了Rectangle,所以通过super()我们知道它使用了Rectangle类的构造函数。

private double side;

public Square(double side)
{
    super(side, side);
    this.side = side;
}

public void print()
{
    System.out.println("I am a square of side " + side);
}

这是我们的主要内容:

Square b = new Square(6.0);
Rectangle c = (Rectangle) b;
c.print();

我们创建一个Square 类型的对象,其中包含两个side 变量double 6.0

接下来,我们将b 转换为Rectangle c,这就是我的问题所在。

为什么c.print() 会打印出I am a square of side 6.0

【问题讨论】:

  • 你希望它打印什么?
  • 那甚至不会编译
  • 如果你的所有类成员都是private,那么使用继承有什么意义?
  • @cagirici 不确定现在是否是在 OPs 学习阶段学习良好实践和设计模式的合适时机——因为他现在才理解该语言(甚至可能是一般的 OOP)。
  • 对于读者(包括试图回答这个问题的人):假设printRectangle类中声明的非虚拟方法,那么这段代码的行为就像@987654341中定义的那样@ 班级。 virtual 允许多态,在 Java 中所有方法默认都是虚拟的。

标签: java inheritance


【解决方案1】:

多态

在 (Java) 中,继承是预期的行为——多态——它是您(开发人员)围绕一个概念(矩形)设计应用程序并允许其他相关概念(正方形)用于使用原始概念(矩形)但具有自己(正方形)行为的地方。

实用性

假设您有一个矩形列表或数组,并且您将使用从您自己的包外部的函数返回接收到的对象填充它。然后,您将遍历列表并要求每个对象执行某些操作——希望这些对象表现得像它们真实的样子,而不是它们要填充的东西,这是正常的。

如果你问一个矩形它的面积是多少,它将乘以长度和宽度并返回结果。如果你不在 square 类中覆盖它,它会做同样的事情,但你可以覆盖它,它可以将其面积计算为 Math.pow(this.side, 2)

这个继承链怎么样:

Shape > Polygon > Quadrilateral > Parallelogram > Rectangle > Square

您肯定需要实现不同的面积计算方法——您不希望每个对象都按照其自己的底层结构告诉它的行为(而不是像它被强制转换为的类型)吗?

【讨论】:

    【解决方案2】:

    我认为这是您需要的修改:

    public static class Rectangle
    {
        protected double length;
        protected double width;
        public Rectangle(double length, double width)
        {
            this.length = length;
            this.width = width;
        }
        public Rectangle(Rectangle r)
        {
            this.length = r.length;
            this.width = r.width;
        }
        public void print()
        {
             System.out.println("I am a rectangle of length " + length + " and width " + width);
        }
    }
    public static class Square extends Rectangle
    {
    
    
        public Square(double side)
        {
            super(side, side);
    
        }
    
        public void print()
        {
            System.out.println("I am a square of side " + length);
        }
    }
    
    public static void main(String[] args)
    {
        Square b = new Square(6.0);
        Rectangle c = new Rectangle(b);
        c.print();
    }
    

    您的Square 课程与Rectangle 课程无关。它具有不同的属性。如果你使用继承,基类应该继承一些东西。

    另外,Rectangle 中没有 print() 方法。

    一个简单的复制构造函数,使您的基类成员protected 并删除不必要的派生类成员应该可以工作。

    【讨论】:

      【解决方案3】:

      这假设 Rectangle 声明了一个 print() 方法。

      这个动作

      Rectangle c = (Rectangle) b;
      

      不对b 引用的实例做任何事情。它只是将引用变量b 扩展为其超类型Rectangle

      调用print() 将具有多态行为,并且在运行时,将使用Square 的实现,因为c 正在引用Square 对象,该对象已将size 设置为6

      Square b = new Square(6.0);
      ...
      private double side;
      
      public Square(double side) {
          super(side, side);
          this.side = side;
      }
      

      请注意,这是自 all methods are virtual by default 以来 Java 中的预期行为。在 C++ 和 C# 等其他语言中,由于 Rectangle 中的 print 方法未声明为 virtual,因此您的演员表会起作用。

      更多信息:

      【讨论】:

      • 这是否意味着即使c 是对象类型Rectangle,它仍然引用b,因为正如您所说,c 引用的是Square 对象?
      • @theGreenCabbage 您必须区分变量和实例。变量引用实例。该变量具有声明的类型(或静态类型)并引用具有动态类型(运行时类型)的实例。由于SquareRectangle,因此您可以使用声明类型为Rectangle 的变量来引用它,但对象仍然是Square
      • @theGreenCabbage 这是正确答案,但我提供了另一个答案来补充此信息。
      • 我们是否可以假设无论我们有多少扩展类,使用强制转换都不会改变超类的行为?
      • @theGreenCabbage 是的,您可以假设 对于 Java
      【解决方案4】:

      请记住,c 只是对内存中对象的引用。被引用的对象是 dynamic 类型的 Square,尽管引用 cstatic 类型的 Rectangle。静态类型和动态类型之间的区别对于本示例至关重要。

      与对象引用关联的静态类型是编译器用来确保我们的代码从类型角度来看有意义的类型。然而,当调用对象的方法时,调用哪个方法取决于对象的动态类型,即在运行时实际引用的对象类型。这称为动态绑定

      还值得一提的是,矩形-正方形示例是典型示例。它通常用于说明将类型和超类型视为类别和概括可能会产生误导。遵循 Liskov 替换原则需要以另一种方式编写类,使用 Rectangle extends Square。这是面向对象编程的另一个重要思想。

      【讨论】:

        【解决方案5】:

        因为继承,每个Square也是一个Rectangle。当您将其转换为将Square 推入Rectangle 变量时,它会保留其Squareness。在此示例中,SquareRectangle 可以放入您的 c。多态性意味着将使用它真正的任何类上的方法。

        【讨论】:

          【解决方案6】:

          这是多态行为。类的实际类型决定了调用的方法。将调用Squareprint 版本,因为对象的实际类型是Square

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2015-01-11
            • 2015-03-25
            • 2020-06-07
            • 2021-07-11
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多