Reputation: 8407
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
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
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
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
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
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
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
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
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
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