多态是否适用于Java中的类属性?

我知道当使用父类引用来引用子类对象时,OOP中多态性的常见用法是这样的:

Animal animal = new Animal(); Animal dog = new Dog(); 

我知道多态性适用于类方法,但它是否也适用于类属性? 我尝试用这个小例子来测试它:

 public class Main{ public static void main(String args[]){ Animal animal = new Animal(); Animal dog1 = new Dog(); Dog dog2 = new Dog(); System.out.println("Animal object name: " + animal.name); System.out.println("Dog1 object name: "+dog1.name); System.out.println("Dog2 object name: " + dog2.name); animal.print(); dog1.print(); dog2.print(); } } class Animal{ String name = "Animal"; public void print(){ System.out.println("I am an: "+name); } } class Dog extends Animal{ String name = "Dog"; public void print(){ System.out.println("I am a: "+name); } } 

这是输出:

 Animal object name: Animal Dog1 object name: Animal Dog2 object name: Dog I am an: Animal I am a: Dog I am a: Dog 

正如您所看到的(我希望它很清楚),多态性与print()方法一起正常工作,但是对于class属性“name”,它取决于引用变量。

所以,我是对的吗? 多态性不适用于类属性?

扩展类时,方法会被覆盖,但字段会被隐藏。 动态分派适用于方法,但不适用于字段。 为什么语言设计如此,天知道为什么。

不,它没有。 实例变量是特定类的属性,不受超级或子类和多态直接影响。

您仍然可以通过在Dog中使用“super.name”和“this.name”来访问这两个字段,但如果您只使用“名称”,那么Dog中的那个将接管。 如果你想要另一个你明确需要调用super。 请注意,我正在谈论访问Dog类中的变量。

Dog.name 隐藏了 Animal.name ,这是一个非常糟糕的模式。 任何好的IDE都会警告你,你正在这样做。

两个实例字段都存在,您可以从Dog作为this.namesuper.name访问它们。

动物的领域被Dog的领域隐藏,您仍然可以通过引用它来访问Animal的领域。

您期望的行为可以通过以下方式实现:

 public class Main{ public static void main(String args[]){ Animal animal = new Animal(); Animal dog1 = new Dog(); Dog dog2 = new Dog(); System.out.println("Animal object name: " + animal.name); System.out.println("Dog1 object name: "+dog1.name); System.out.println("Dog2 object name: " + dog2.name); animal.print(); dog1.print(); dog2.print(); } } class Animal { String name = "Animal"; public void print(){ System.out.println("I am an: "+name); } } class Dog extends Animal{ public Dog() { this.name = "Dog" } } 

变量在Java中不是多态的; 他们不会互相覆盖。

编辑:为了进一步支持Solver的答案,我记得我的OOP老师声称当你使用Parent classreference创建Child class对象时 ,在Parent class中不存在的Child class变量仍然在运行时分配内存但不能可以访问,因为Parent class中没有可以访问Child class 变量的方法。

基本上,当一个父类有一个孩子时,子类应该完全看起来像它的父母,否则“如何称他们为父母和孩子?” 对? 无论如何,允许子类具有与其父类不同的行为。 这很有意义和自然。

但是如果你想要覆盖子类的属性,你可以通过构造函数机制来完成它

代码示例

 class Animal{ String name; public Animal(){ name = "Animal"; } public Animal(String name){ this.name = name; } public void print(){ System.out.println("I am an: "+name); } } class Dog extends Animal{ Dog(){ super("Dog"); } public void print(){ System.out.println("I am a: "+name); } } 

您将看到Dog类中的属性名称"Dog"通过构造函数传递,在这里我们可以通过关键字super调用父类的构造函数。

结果:

 Animal object name: Animal Dog1 object name: Dog Dog2 object name: Dog I am an: Animal I am a: Dog I am a: Dog 

animal上调用print方法时,JVM首先在dog对象中搜索print方法。 如果dog对象中没有print方法,JVM会搜索Dog的超类。 因为它在Dog类中找到了print方法,所以它开始执行它。 Dog类中的name字段隐藏了从Animal类inheritance的name字段。 它就像:

 public class Test { static String name = "xyz"; public static void main(String[] args) { { String name = "abc"; System.out.println(name); // abc is printed } System.out.println(name); // xyz is printed } } 

在块内,有一个局部变量name 。 因此隐藏了全局变量name 。 但是当你离开区块时,局部变量就会生效。

注意:

Dog类应该像:

 class Dog extends Animal{ this.name = "Dog"; public void print(){ System.out.println("I am a: " + this.name); } } 

你做的是一个糟糕的设计。

在Java中,我们使用set和get方法来访问字段。 在您的示例中,我们有一个Dog类扩展Animal类。

但是如果你把它声明为动物,如果你直接调用字段Amimal dog1 = new Dog(); 你创建一个Dog实例,但声明为Animal,所以当你调用dog1.name ,它会给你Animal的值。