【问题标题】:Confused with this simple example of polymorphism对这个简单的多态性示例感到困惑
【发布时间】:2023-03-24 08:23:01
【问题描述】:

请看一下这段代码:

class Foo {
    public int a;
    public Foo() {
        a = 3;
    }
    public void addFive() {
        a += 5;
    }
    public int getA() {
        System.out.println("we are here in base class!");
        return  a;
    }
}

public class Polymorphism extends Foo{
    public int a;
    public Poylmorphism() {
        a = 5;
    }
    public void addFive() {
        System.out.println("we are here !" + a);
        a += 5;
    }
    public int getA() {
        System.out.println("we are here in sub class!");
        return  a;
    }

    public static void main(String [] main) {
        Foo f = new Polymorphism();
        f.addFive();
        System.out.println(f.getA());
        System.out.println(f.a);
    }
}

这里我们将Polymorphism 类的对象的引用分配给Foo 类型的变量,经典多态。现在我们调用方法addFive,它已在类Polymorphism 中被覆盖。然后我们从一个 getter 方法打印变量值,该方法在类多态性中也被覆盖。所以我们得到答案为 10。但是当公共变量 a 被 SOP 化时,我们得到答案 3!!

这是怎么发生的?即使引用变量类型是 Foo 但它指的是多态类的对象。那么为什么访问f.a 不会导致Polymorphism 类中的a 值被打印出来呢?请帮忙

【问题讨论】:

    标签: java polymorphism


    【解决方案1】:

    您隐藏了Polymorphisma - 您实际上应该为此收到编译器警告。因此,这是两个不同的 a 字段。与方法相反,字段不能是虚拟的。好的做法是根本没有公共字段,而只有改变私有状态的方法(封装)。

    如果你想把它变成虚拟的,你需要用访问器方法把它变成一个属性(例如你有什么:getA)。

    【讨论】:

    • 可能会澄清这一点的一件事是查找“alpha 重命名”的定义,这在道德上正在发生。
    • 为什么调用方法时检查对象类型,获取字段时检查类类型?如果对象类型也引用了变量,结果会是什么?
    • @Shades88,查找虚拟字段意味着执行“getter”代码。字段是对象数据中的偏移量,而方法与实际对象数据无关;它们的地址存储在一个虚拟方法表中,每个类只存在一次。方法只是传递一个指向对象数据的指针,它们不是数据的一部分。请注意,一旦字段是私有的,这无论如何都变得无关紧要,因为除了声明该字段的类的方法之外没有其他代码可以看到它。
    • @Shades88,我确实做过一些与编译器相关的事情,尽管不是在 Java 世界中。快速回顾:“最终”(非虚拟)方法在内存中具有固定地址,它们被调用并且第一个参数是“this”(例如实例)。请注意,调用本身不需要了解或访问实例。字段访问是不同的:对于给定类型,知道字段数据存储在实例数据的哪个偏移量处。所以 this+field_offset 指向字段数据。现在在常见的 OO 运行时中,第一个字段是指向类数据的隐藏字段。 (续)
    • 这个类数据不仅用于确定类型,还保存了一个虚方法表(VT)。因此,就像在实例数据中已知字段的偏移量一样,每个虚拟方法在 VT 中都有其指针的已知偏移量,当调用虚拟调用时,编译器只需加载该方法的 VT 条目并调用那个指针。请注意,这对实例的内存布局没有任何影响,并且对于相同的方法,VT 中的索引也始终相同。因此,可以说字段在设计上是虚拟的 - 除非您执行 alpha 重命名,就像使用方法一样。
    【解决方案2】:

    这是因为您不能覆盖类变量。当访问一个类变量时,引用的类型,而不是对象的类型,决定了你会得到什么。

    如果您在子类中删除 a 的重新声明,那么我认为行为会更符合预期。

    【讨论】:

    • 哦..那你能解释一下为什么会这样吗?我的意思是,如果变量也被覆盖,可能会产生什么影响?因为在调用方法时检查对象类型和在获取字段时检查类类型听起来很奇怪
    • 我可以试试 ;) 引用类型决定了哪些方法和变量可用于调用。您对多态性所做的就是覆盖这些可用方法的行为。但是,由于不能覆盖类变量,所以总是会在引用类型的类中获取变量的值。
    猜你喜欢
    • 2020-08-21
    • 2018-09-29
    • 1970-01-01
    • 1970-01-01
    • 2016-01-16
    • 2011-09-17
    • 2016-11-13
    • 2014-10-08
    • 1970-01-01
    相关资源
    最近更新 更多