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

Does polymorphism apply on class attributes in Java?

提问人:Toni Joe 提问时间:8/30/2015 最后编辑:Toni Joe 更新时间:8/30/2015 访问量:6089

问:

我知道 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()方法一起工作得很好,但是对于类属性“name”,它取决于引用变量。

那么,我说得对吗?多态性不适用于类属性?

Java 多态性

评论

1赞 T.J. Crowder 8/30/2015
只是FWIW,你所说的“类变量”的正确术语是“实例字段”。“类变量”使人们思考类域,例如,类域。static
0赞 Carcigenicate 8/30/2015
我更新了标题以反映上述建议。如果我更改了含义,请随时回滚。
0赞 Pshemo 8/30/2015
“字段不是多态的”< - 一个需要记住的简单规则。

答:

8赞 Zarwan 8/30/2015 #1

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

您仍然可以在 Dog 中使用“super.name”和“this.name”访问这两个字段,但如果您只使用“name”,则 Dog 中的字段将接管。如果你想要另一个,你明确需要调用 super。请注意,我说的是访问 Dog 类中的变量。

评论

0赞 Zarwan 8/30/2015
@Codebender如果 name 是 Dog 和 Animal 中的实例变量,并且您在 Dog 类中调用 name,我相信你会找回 Dog 的名字。
0赞 Zarwan 8/30/2015
@Codebender我的意思是在 Dog 类中调用名称,而不是在它的实例上。我编辑了这个澄清。
0赞 RamanSB 8/30/2015
@CodeBender是正确的,则在使用 obj.name 时,可以从引用类 Animal(Not Dog) 访问这些字段。
0赞 Zarwan 8/30/2015
@RamanSB 我同意他/她是对的,但我说的是从 Dog 类内部叫名字。我编辑了这个澄清。
1赞 8/30/2015
@Zar 严格的 OOP 术语定义类的属性,而不考虑语言引用实现。这个术语取自 Smalltalk 时代,当时 OOP 编程被烧毁。
5赞 Andreas 8/30/2015 #2

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

这两个实例字段都存在,您可以从 as 和 .Dogthis.namesuper.name

评论

0赞 8/30/2015
什么 IDE 会警告什么?
0赞 Andreas 8/30/2015
@nikpon我使用 Eclipse,我知道它确实如此。我认为 NetBeans 也会。--- 在 Eclipse 中,警告称为 字段声明隐藏了另一个字段或变量
0赞 8/30/2015
这只是一个警告,让你更好地编写代码,但它不是一种模式,恕我直言,如果语言允许,这还不错。否则,这将是一门糟糕的语言。
3赞 Andreas 8/30/2015
@nikpon 这太不对了。仅仅因为你可以做某事,并不能使它成为可能。获取任何源文件,将所有字段和变量名称更改为 30 个字符的随机名称,然后从源文件中删除所有注释和换行符,使整个文件为一行。这是完全有效的,可以毫无问题地编译和运行,但仍然非常错误
2赞 burglarhobbit 8/30/2015 #3

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

编辑:为了进一步支持求解者的回答,我记得我的 OOP 老师声称,当你创建一个 的对象时,其中不存在的变量在运行时仍然分配内存,但无法访问,因为没有方法可以访问 的变量Child classreferenceParent classChild classParent classParent classChild class

评论

1赞 Vince 8/30/2015
这不是浪费内存。它是一个设计方面,允许您通过接口与对象进行交互,并能够通过不同的类型引用对象来更改该接口。该对象可以同时由变量和变量引用。如果强制转换,您可以将其从 var 传递给 var。ChildChildParentChildParent
0赞 burglarhobbit 8/30/2015
哇,好深啊!今天学到了一些新东西。编辑。
0赞 8/30/2015
这如何解释为什么多态变量的行为不像多态。
0赞 Tripp Kinetics 4/8/2021
为了补充@Dioxin所说的内容,还可以通过从引用 中进行类型转换来访问 中的变量。ChildParent
5赞 Torge 8/30/2015 #4

Animal 字段被 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"
    }
}

评论

0赞 8/30/2015
添加默认构造函数如何改变任何内容?
0赞 Torge 8/30/2015
嗯,你是对的。我将其与在 Animal 中定义非默认构造函数时混淆了。stackoverflow.com/questions/7187799/......我删除了它并再次更新,以免弄乱答案。
10赞 user4039871 8/30/2015 #5

扩展类时,方法将被重写,但字段将被隐藏。动态调度适用于方法,但不适用于字段。为什么语言设计得如此,天知道为什么。

评论

1赞 8/30/2015
我们知道这一点,但我们不是神。
0赞 8/30/2015
规则是你可以投赞成票好的答案和反对票坏的答案。不幸的是,我没有时间提供答案,但只有一个被否决的答案是正确的。许多投赞成票的答案并不能正确回答问题。随意对它们投反对票,我还在等待,但这个答案是可以接受的。我不知道 OP 为什么要这样做,但 AFAIK 是一个很好的答案并不总是被接受的答案,甚至没有得到高度的赞成。
0赞 8/30/2015
恕我直言,你什么都没有,它甚至没有关闭正确答案。要么你很匆忙,要么你根本不懂语言。反正你有时间详细说明,所以已经晚了。
0赞 8/30/2015
你不明白,我说 1) 它没有提供正确答案,2) 它没有解决 OP 的问题,3) 它的质量较低。
0赞 fabien t 8/30/2015 #6

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

但是,如果您将其声明为 Animal,则直接调用该字段将创建一个 Dog 实例,但声明为 Animal,因此当您调用时,它会为您提供 Animal 的值。Amimal dog1 = new Dog();dog1.name

评论

0赞 Makoto 8/30/2015
等等,什么?我不确定我是否遵循这里。这如何解释正在发生的事情?
0赞 8/30/2015
这是正确的,但是 polimorphism 呢?说政治主义不适用于实例变量,那么它在哪里应用呢?
0赞 fabien t 9/26/2015
每种编程语言都有其多态性实现!因此,当您尝试解决方案时,您需要按照语言给出的良好做法/规范而不是?也许,我需要花更多时间来解释,我试图提供帮助......我会改进这个谢谢
1赞 Wololo 8/30/2015 #7

当您调用 method on 时,JVM 会首先在对象中搜索方法。如果对象中没有方法,JVM 将搜索 的超类。由于它在类中找到方法,因此它开始执行它。类中的 name 字段隐藏从类继承的 name 字段。它就像:printanimalprintdogprintdogDogprintDogDogAnimal

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
}
}

在块内部,有一个局部变量。所以全局变量是隐藏的。但是当你离开块时,局部变量就会生效。namename

注意:

Dog类应该是这样的:

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

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

3赞 fronthem 8/30/2015 #8

基本上,当一个父类有一个子类时,子类应该完全看起来像它的父类,否则“你怎么能称他们为父类和子类?”对吧?无论如何,子类被允许具有与其父类不同的行为。这是很有道理和自然的。

但是,如果您确实想覆盖子类中的属性,则可以通过构造函数机制来完成

代码示例

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"Dogsuper

结果:

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