Toni Joe
Toni Joe

Reputation: 8407

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?

Upvotes: 16

Views: 6623

Answers (9)

Haitam-Elgharras
Haitam-Elgharras

Reputation: 138

The behavior you're seeing is due to how Java handles variables vs methods when it comes to inheritance. You're right - polymorphism doesn't work with instance variables! Let me explain why.

In your code:

Animal dog1 = new Dog();
System.out.println("Dog1 object name: "+dog1.name);  // Prints "Animal"

Even though dog1 contains a Dog object at runtime, you still see "Animal" because variables use something called "static binding" - they're resolved based on the reference type (Animal in this case). This is different from methods, which use "dynamic binding" - they're resolved based on the actual object type at runtime.

That's why your print() method works as expected:

dog1.print();  // Prints "I am a: Dog"

The method gets resolved at runtime, sees that dog1 is actually a Dog object, and calls Dog's version of print().

But You don't actually need to declare the name variable in both parent and child classes, it's better to declare it once in the parent class and use the parent's constructor. When you declare the same variable in both classes, you're just hiding the parent's variable (this is called "shadowing"), which can lead to confusion.

A better way to handle this:

class Animal {
    private String name;
    public Animal(String name) {
        this.name = name;
    }
    public String getName() { return name; }
}

class Dog extends Animal {
    public Dog() {
        super("Dog");  // Use parent's constructor
    }
}

Now you can do:

System.out.println(dog1.getName());  // Will print "Dog"

Upvotes: 0

Torge
Torge

Reputation: 2284

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

Upvotes: 5

fronthem
fronthem

Reputation: 4139

Basically, when a parent class has a child, child class should totally looks like its parent otherwise "How you can call them a parent and child?" right? Anyway, child class is allowed to have a behavior different from its parent. That's quite make sense and natural.

But if you do want to override attribute from a child class you can be done it via constructor mechanism

Code Example

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

You will see that attribute name "Dog" in Dog class is passed via constructor which here we can call constructor of parent class via a keyword super.

Results:

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

Upvotes: 3

burglarhobbit
burglarhobbit

Reputation: 2291

Variables are not polymorphic in Java; they do not override one another.

Edit: To further support Solver's answer, I remember my OOP teacher claiming that when you create an object of Child class with a reference of Parent class, the variables in Child class which doesnot exist in Parent class are still allocated memory in the runtime but cannot be accessed as there are no methods in Parent class which could access a variable of the Child class.

Upvotes: 2

fabien t
fabien t

Reputation: 358

In Java we use set and get method for accessing a field. In your example, we have a Dog class extending an Animal class.

But if you declare it as an Animal, if you call directly the field Amimal dog1 = new Dog(); you create a Dog instance, but declared as Animal, so when you call dog1.name, it give you the value of Animal.

Upvotes: 0

Wololo
Wololo

Reputation: 861

When you invoke print method on animal, JVM searches for a print method in dog object first. If there were no print method in dog object, JVM would search the super class of Dog. Since it finds the print method in Dog class, it starts to execute it. The name field in the Dog class hides the name field which was inherited from the Animal class. Its just like:

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

Inside the block, there is a local variable name. So the global variable name is hidden. But when you get out of the block, the local variable comes in effect.

NOTE:

Dog class should be like:

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

What you did is a bad design.

Upvotes: 1

Zarwan
Zarwan

Reputation: 5797

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.

Upvotes: 8

user4039871
user4039871

Reputation:

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.

Upvotes: 10

Andreas
Andreas

Reputation: 159106

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.

Upvotes: 5

Related Questions