Does polymorphism apply on class attributes in Java?

I know that the common use of polymorphism in OOP occurs when a parent class reference is used to refer to a child class object like this:

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

And I know that polymorphism applies on class methods, but does it also apply on class attribute? I tried to test that with this little example:

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

And this is the output:

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

As you can see (I hope it's clear), the polymorphism works fine with the print() method, but with class attribute "name", it depends on the reference variable.

So, am I right? the polymorphism doesn't apply on class attributes?


Solution 1:

When you extend a class, methods are overriden, but fields are hidden. Dynamic dispatch works for methods, but not for fields. Why is the language designed so, god knows why.

Solution 2:

No, it doesn't. Instance variables are properties of a specific class, and are not affected directly by super or sub classes and polymorphism.

You can still access both fields by using "super.name" and "this.name" in Dog, but if you use just "name" the one in Dog will take over. If you want the other one you explicitly need to call super. Note that I'm talking about accessing the variables in the Dog class.

Solution 3:

Dog.name is hiding Animal.name, and it is a very bad pattern to do that. Any good IDE will warn you that you're doing it.

Both instance fields do exist, and you can access both from Dog as this.name and super.name.

Solution 4:

The field of Animal is hidden by the field of Dog, you can still access the field of Animal by referencing it as you did.

The behaviour you expect can be achieved this way:

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