派生 class 的意外行为
Unexpected behaviour of derived class
我有一个简单的程序如下:
class Foo {
private final String str = getClass().getSimpleName();
protected void print(){
System.out.println(str);
}
}
class Bar extends Foo {
private final String str = getClass().getSimpleName();
@Override
protected void print(){
super.print();
System.out.println(str);
}
}
class FooBarMain {
public static void main(String[] args){
Foo foo = new Foo();
foo.print();
Foo foobar = new Bar();
foobar.print();
Bar bar = new Bar();
bar.print();
}
}
输出:
福
酒吧
酒吧
酒吧
酒吧
输出不应该是下面这样吗?
福
福
酒吧
福
酒吧
查看输出,str
似乎已在派生 class 中被覆盖。但,
str
是最终的和私有的。没有办法可以覆盖它。谁能帮我?
Bar 中的成员 str 隐藏了 Foo 中的成员,所以如果你有一个 Bar 对象,它总是打印 "Bar"。不管持有对象句柄的变量是什么类型,类型是Foo还是Bar,只要你创建一个Bar,它就是一个Bar,不管你把它当作Bar、Foo还是Object。
并不是说 str
被覆盖了。它只是在您构造对象时进行计算。您的 Bar
class 有两个不同的 str
值,每个值都是在您使用 getClass().getSimpleName();
.
初始化对象时确定的
即使 Bar
中的 super.print()
引用 Foo
class 中的 str
,该值仍在使用 [= Bar
对象的 18=] 方法。
一旦您实际 运行 代码,getClass()
就不会在意您将它写在 Foo
class 声明中。它所知道的只是计算时对象的实际 class 是什么。
来自 getClass
的文档
Returns the runtime class of this Object. The returned Class object is the object that is locked by static synchronized methods of the represented class.
因为你的方法不是静态的,它是在构造对象时计算的,而不是在声明 class 时计算的。在那个时间点,一个Bar
是一个Bar
,所以为了遵守它所承诺的合同,getClass
需要returnBar.class
。
其实很简单:您使用 getClass()
表示 this.getClass()
。如果实例化 Bar
,那么 getClass()
将 return class Bar
- 无论是在 Foo
还是在 Bar
中调用:无论哪种方式,class 都是 Bar
.
获得预期输出的最简单方法就是执行
class Foo {
private final String str = "Foo";
protected void print(){
System.out.println(str);
}
}
class Bar extends Foo {
private final String str = "Bar";
@Override
protected void print(){
super.print();
System.out.println(str);
}
}
getClass()
并没有按照你的想法去做。它不 return class 行 getClass
出现的地方,它 return 是实例的实际运行时类型。因此,如果对象是使用 new Bar()
实例化的,getClass()
将 总是 给你 Bar.class
即使代码出现在 Foo
甚至如果变量的编译时类型是 Foo
(如您的 Foo fooBar = new Bar();
示例)。
我有一个简单的程序如下:
class Foo {
private final String str = getClass().getSimpleName();
protected void print(){
System.out.println(str);
}
}
class Bar extends Foo {
private final String str = getClass().getSimpleName();
@Override
protected void print(){
super.print();
System.out.println(str);
}
}
class FooBarMain {
public static void main(String[] args){
Foo foo = new Foo();
foo.print();
Foo foobar = new Bar();
foobar.print();
Bar bar = new Bar();
bar.print();
}
}
输出:
福
酒吧
酒吧
酒吧
酒吧
输出不应该是下面这样吗?
福
福
酒吧
福
酒吧
查看输出,str
似乎已在派生 class 中被覆盖。但,
str
是最终的和私有的。没有办法可以覆盖它。谁能帮我?
Bar 中的成员 str 隐藏了 Foo 中的成员,所以如果你有一个 Bar 对象,它总是打印 "Bar"。不管持有对象句柄的变量是什么类型,类型是Foo还是Bar,只要你创建一个Bar,它就是一个Bar,不管你把它当作Bar、Foo还是Object。
并不是说 str
被覆盖了。它只是在您构造对象时进行计算。您的 Bar
class 有两个不同的 str
值,每个值都是在您使用 getClass().getSimpleName();
.
即使 Bar
中的 super.print()
引用 Foo
class 中的 str
,该值仍在使用 [= Bar
对象的 18=] 方法。
一旦您实际 运行 代码,getClass()
就不会在意您将它写在 Foo
class 声明中。它所知道的只是计算时对象的实际 class 是什么。
来自 getClass
Returns the runtime class of this Object. The returned Class object is the object that is locked by static synchronized methods of the represented class.
因为你的方法不是静态的,它是在构造对象时计算的,而不是在声明 class 时计算的。在那个时间点,一个Bar
是一个Bar
,所以为了遵守它所承诺的合同,getClass
需要returnBar.class
。
其实很简单:您使用 getClass()
表示 this.getClass()
。如果实例化 Bar
,那么 getClass()
将 return class Bar
- 无论是在 Foo
还是在 Bar
中调用:无论哪种方式,class 都是 Bar
.
获得预期输出的最简单方法就是执行
class Foo {
private final String str = "Foo";
protected void print(){
System.out.println(str);
}
}
class Bar extends Foo {
private final String str = "Bar";
@Override
protected void print(){
super.print();
System.out.println(str);
}
}
getClass()
并没有按照你的想法去做。它不 return class 行 getClass
出现的地方,它 return 是实例的实际运行时类型。因此,如果对象是使用 new Bar()
实例化的,getClass()
将 总是 给你 Bar.class
即使代码出现在 Foo
甚至如果变量的编译时类型是 Foo
(如您的 Foo fooBar = new Bar();
示例)。