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
方法 aaa
和 bbb
。在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.
希望这对您有所帮助。
例如,当我有一个 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
方法 aaa
和 bbb
。在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.
希望这对您有所帮助。