多态是否适用于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.name
和super.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 class
的reference
创建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的值。