Java。 class 的类型与对象

Java. Type vs Object of a class

例如,当我有一个 class 矩形和一个 class 正方形时。如果 class 方形扩展了矩形,那么 class 方形是 class 矩形的子 class。现在说我有代码。 Rectangle shape = new Square(5,6); 我会创建一个对象,但它是 Rectangle 类型,但使用 square 的构造函数或 Square 的对象?,我的困惑始于 class Square 具有与 class Rectangle 相同的方法,但它会使用 class Square 而不是 class Rectangle 中的方法。那么我是否创建了一个 Square 对象,但类型为 Rectangle?

评论:知道正方形不扩展矩形,每个人都很生气,但这是我老师的例子,纯粹是展示继承,事实上,最后他展示了如果我们使用这段代码,它会产生一个错误。

这就是所谓的多态性,做你的功课。 shape 变量只是一个参考,实际对象是一个 Square 对象。

继承总是给你的对象一个is-a relationship。因此,凭借 Square 扩展 Rectangle,可以安全地说 Square Rectangle.

您已经创建了 Square 的实例,但您将只能在其上使用其父 Rectangle 提供的方法。

您创建了一个 Square 类型的对象。

调用 new Foo(...)总是 创建一个 Foo,无一例外。它将始终调用 Foo 的构造函数,同样无一例外。也就是说,如果 Foo 从另一个 class 扩展而来,那么 Foo 的构造函数要做的第一件事就是调用 superclass 的构造函数。同样,这没有例外,永远 — 尽管您不一定会在代码中看到它,因为如果您没有调用 super(...) 并且在 super class,那么编译器会自动为你调用那个构造函数。但无论如何,它总会被调用。

由于您已将 Square 设置为矩形,因此该对象也是 Rectangle 的一个实例。对于您的 classes,正方形始终是长方形,但并非所有长方形都是正方形。

Rectangle shape 位仅表示就编译器 而言,它是"at least" 一个矩形。它可能恰好是一个矩形,也可能是矩形的子class(如方形)——但它不会是一个数字。

当您调用 shape.getArea()(例如)时,JVM 将查找 actual 类型的 shape — 而不仅仅是它的编译时类型,但它实际上是在您调用 new 时创建的类型 — 并调用 class 定义的方法。

您要查找的是问题 "Is Square really a rectangle?" 的答案。从宝石学的角度来看,答案是肯定的,但从软件的角度来看,答案可能并不那么明显。 "Is-a" 关系必须遵循 Liskov Substitution principle

所以本质上,如果基础 class 有一些不适用于派生 class 的方法,那么它就不是 "is-a" 关系。

例如

class 矩形{

/构造函数代码/

public void changeWidth(){....} public void changeLength(){....}

}

changeWidth 和 changeLength 是两个函数,它们对矩形非常有意义,但对正方形则不然,因为正方形的所有边都相等。虽然你可以破解并在方块中给出你自己的两个函数的实现 class 但从我的角度来看,关系看起来并不自然。

其他回答我同意

这让很多人感到困惑。让我试着分解一下。

当你说 new Square 时,对象是使用 Square 构造函数创建的。该对象在其存在期间将具有 Square 类型。

当您声明类型为 Rectangle 的变量时,即 Rectangle x;Rectangle x = (anything); 您是在告诉编译器 x(当它不为空时)将始终是对 Rectangle 或其任何子类 (包括 Square)的引用。当您说 Rectangle x = new Square(...) 时,x 将引用 Square,即 Rectangle但是x 稍后可以重新分配成为其他 Rectangle,而不是 Square

也就是说,当你说x.method(...)时,编译器只允许你使用Rectangle中定义的方法,因为编译器只知道x是一个Rectangle 或某个子类。如果您在 Square 中声明了一个新方法,则不能在上述调用中使用它。

但是,如果x仍然是对Square的引用,那么当你调用Rectangle中定义的方法时,程序实际上会运行该方法你为 Square 写的(如果你覆盖了 Rectangle 中的那个)。

也许这个例子会有所帮助。假设 Rectangle 声明了 public 方法 aaabbb。在Square中,你写了一个覆盖方法aaa,你没有覆盖bbb,你声明了一个新的public方法ccc.

Rectangle x = new Square(10);
Rectangle y = new Rectangle(5,6);

// assume that x and y aren't changed 

x.aaa();   // runs the overriding method aaa in Square
y.aaa();   // runs the method aaa in Rectangle

x.bbb();   // runs the method in Rectangle, since it's not overridden.  But
           // if bbb calls aaa, then it will call the aaa in Square.
y.bbb();   // runs the method in Rectangle

x.ccc();   // illegal.  Even though the object is actually a Square, the
           // compiler isn't allowed to know that.
y.ccc();   // illegal

((Square)x).ccc();  // This is how you can get to the new method that you
           // declared in Square.  Even though the compiler doesn't know 
           // that x is a Square, when you use the cast, you tell the 
           // compiler that it's OK fo treat it as a Square, and to access
           // the method defined only in Square.
((Square)y).ccc();  // Will throw ClassCastException at runtime, because 
           // y isn't a Square.

希望这对您有所帮助。