派生 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(); 示例)。