Polymorphism on member variables. I know it is not possible, I don't understand this specific case
Asked Answered
B

5

11

I know the behavior, but I am not 100% sure on why this occurs.
I understand that there is no polymorphism for instance variables in java. The variables are resolved statically by the compiler.
But in the following I am confused on something:

class Animal{  
   String name = "Animal";  
   public void display(){  
    System.out.println("My name is "+ name);  
   }  
}  

public class Dog extends Animal {   
   String name = "Dog";   

   public static void main(String[] args) {  
        Animal a = new Dog();  
        Dog d = new Dog();  
        System.out.println(a.name);//Line 1  
        a.display();//Line 2   
        d.display();//Line 3  
   }  
}  

I undertand that in Line 1 it will display Animal as it is the static type of a (resolved by compiler).
What confuses me is why Line 3 will also display My name is Animal?
The method will be tried to be called on Dog since this is the actual object at runtime and since it is not overidden the method will be found in the parent class Animal.
What I don't get is why the name of the parent class is used inside the method display if the actual object operated on is a Dog. Doesn't it hide the parent's name variable? It does not seem to me like it is statically resolved since the type is Dog. Isn't it part of the oject's memory layout?
It is like inside display only the parent's variable is visible. Why?

Update:

The answers by @Razvan and @LouisWasserman have been helpful.
I have 1 last question after these:
The point from both seems to be the following:
From @Razyan
System.out.println("My name is "+ this.name); //<-- note the this
From @Louis
That the this refers to Animal and that the implementation of display() is in the Animal class.

So far ok. But how are these points consistent with the fact that if I modify display() as follows:

class Animal{  
   String name = "Animal";  
   public void display(){  
    System.out.println("Current class is "+ this.getClass().getName());  
    System.out.println("My name is "+ name);  
   }  
}  

Then the result of:

 Dog d = new Dog();  
 d.display();  

Current class is Dog
My name is animal

I was expecting that this inside the display would be Animal as I understood the answers here. But it is not. Why?

Blastoderm answered 20/8, 2012 at 21:52 Comment(6)
But the method display is called in the scope of the parent class Animal, and in this scope, the name has been assigned "Animal". There is no hiding there, hiding would come around if you overrided the method display in the subclass Dog.Traitorous
So the actual Dog's object memory layout has both variables?I though the "hiding" made only 1 availableBlastoderm
I do not know what you mean by memory layout, but yes, a Dog object has basically two name attributes: this.name and super.name.Traitorous
The Animal.name field is still the only one visible from the Animal class itself. The Dog.name field is the one used by the Dog method implementations.Jeroboam
You might want to look at this related questionUpdate
@LouisWasserman:You mean the part of the memory layout of Dog that is the Animal?So the fact that the variable hides the parent is not visible to that area?Blastoderm
S
13

When you invoke d.display() the Animal.display() is called, since you don't override it in the Dog class.

So an imaginary implementation of Dog.display() would be something like:

 public void display(){  
    super.display();
 }

And the implementation of Animal.display() is:

 public void display(){  
    System.out.println("My name is "+ this.name); //<-- note the this
 } 

The Animal.display() method is not even aware of the existence of an object Dog and consequently of its name variable

Sallet answered 20/8, 2012 at 22:1 Comment(12)
So when you hide the parent variable, both are kept as part of the object and visible to the corresponding scope/methods?Blastoderm
@Cratylus, there is storage for both fields for the object instance -- one for Animal.name, and one for Dog.name.Offoffbroadway
@veer:So the each field is visible to specific scope only?Blastoderm
@Cratylus: Animal.name is visible from both scopes, since it's public; however, since Animal has no clue there is a subclass named Dog with a name field, the name symbol is resolved as a reference to the local Animal.name field. In Dog, similarly, name will resolve to Dog.name; that being said, you can also access Animal.name using super.name :-)Offoffbroadway
@veer:Animal has no clue there is a subclass named Dog with a name field. But the compiler resolves this.And the current static type is actually a Dog.I am not sure I follow how this mechanism worksBlastoderm
@Blastoderm the compiler resolves the name symbol, yes... though I don't quite see your point. You can't override a field -- only a method. What you're doing is called shadowing the superclass name field.Offoffbroadway
@veer:I am trying to understand how this works.I am not trying to make a pointBlastoderm
@Blastoderm maybe you should read §8.3 of the Java Language Specification; shadowing in general is documented in §6.4.1.Offoffbroadway
@Cratylus: I think you should post a new question if you want a strict, technical answer to your update. If you want an intuitive answer, I can tell you that: "this" (at runtime) is resolved to the object on which the display() method is called (of the type Dog). Hence the getClass() returns Dog. At the same time the name, being a field, is resolved to the name in Animal, the scope in which the display is called.Sallet
@Razvan:But if I post a new question,the example code would be the same.My concern is that it will be closed as a duplicateBlastoderm
@Cratylus: give it a try ! Nothing bad can happen.Sallet
@Razvan:I think that this from JLS applies here:The scope of a declaration of a member m declared in or inherited by a class type C (§8.1.6) is the entire body of C, including any nested type declarations..So the actual object is Dog but the display() method is not in the scope of Dog.name (or better Dog.name is not in the scope at this point).This is why the Animal.name is chosen since it is the only visible at that point.Blastoderm
J
2

This might be a more useful way of thinking about it: the fact that the variables have the same name doesn't matter in the slightest. This code will behave exactly the same as

class Animal{  
   String foo = "Animal";  
   public void display(){  
    System.out.println("My name is "+ foo);  
   }  
}  

public class Dog extends Animal {   
   String bar = "Dog";   

   public static void main(String[] args) {  
        Animal a = new Dog();  
        Dog d = new Dog();  
        System.out.println(a.foo);//Line 1  
        a.display();//Line 2   
        d.display();//Line 3  
   }
}

The point is, the field name in the Dog class is treated as completely, utterly separate from the field name in Animal, in terms of which is seen by which methods. When you refer to a.name directly, it only knows that a is an Animal, so it uses the name field from the Animal class.

Jeroboam answered 20/8, 2012 at 22:12 Comment(3)
But the static type in this case d.display is Dog.So the compiler knows it is a Dog and knows it has a name.The actual operation is not overriden, but I can not follow the mechanism here as to why there is a scope. d has been resolved to a Dog.The display is inherited but operated on a Dog.So I can't follow the mechanism here.Why is there a scope?Hasn't the variable been resolved by the static type?Blastoderm
Yes, d is known to be a Dog, but the implementation of display() is in the Animal class, which doesn't know anything about the name field in the Dog class -- it only knows about the name field in the Animal class. The code I've written above is exactly equivalent to the code you wrote; can you see why it works that way there?Jeroboam
+1, and this is a good way of looking at it. If you have a reference of type Animal, its fields will resolve non-polymorphically to a field on Animal. If a subtype of Animal has a field that happens to have the same name, it doesn't factor in at all.Wolfenbarger
C
2

Here I am summarizing all other's points in one answer so that one can get a clear picture of it without putting much effort.

Conceptually code can be thought of as

class Animal{  
   String name = "Animal";  
   public void display(){  
    System.out.println("My name is "+ this.name);  
   }  
}  

public class Dog extends Animal {   
   String name = "Dog";  
   public void display(){  
    super.display();
 } 

   public static void main(String[] args) {  
        Animal a = new Dog();  
        Dog d = new Dog();  
        System.out.println(a.name);//Line 1  
        a.display();//Line 2   
        d.display();//Line 3  
   }  
} 

Here in the Dog's class we're shadowing name field of Animal class ie. Dog's instance will have two instance variable named name one is Animal.name (from inheritance which holds value "Animal") second is Dog.name(which holds value "Dog"). Dog.name hides Animal.name in Dog class.
At Line-3 display() method is invoked on the instance of Dog.Dog doesn't override display() method of Animal so ultimately Animal.display() method gets invoked. In this method this refers to Dog and as Dog has two namein it, one which is in the scope of Animal gets printed.(because being a superclass Animal is unaware of Dog class and Dog.name)

Coralyn answered 17/9, 2015 at 21:55 Comment(0)
A
1

A simple Junit test run in debug mode shows that there are indeed two 'name' variables in the Dog object. Now, answering the question asked in the latest update of OP:

 class Animal{  
   String name = "Animal";  
   public void display(){  
    System.out.println("Current class is "+ this.getClass().getName());  
    System.out.println("My name is "+ name);  
   }  
} 

Dog d = new Dog
d.display();

Here, 'd' contains two variables -> one is the inherited one, other is the one defined in dog class, both having same name. Now, when display() is invoked on Dog object, since it did not override the display() method of Animal class, JVM runs the display() method of Animal class.

System.out.println("Current class is "+ this.getClass().getName());  

will print Dog because it is the actual object on which the method was invoked.

System.out.println("My name is "+ name);  

will print 'My name is Animal' , because as said before, object of Dog has both variables in it, but once inside the Animal.display() method, the value of variable Animal.name is chosen due to the scope.

Achilles answered 1/4, 2016 at 18:46 Comment(0)
U
0
class RectangleFigure {
    private int length, width;

    public RectangleFigure(int length, int width) {
        this.length = length;
        this.width = width;
    }

    public String toString() {
        return "Rectangle: " + "Length = " + length + ", Width = " + width;
    }
}

class BoxFigure extends RectangleFigure {
    private int height;

    public BoxFigure(int length, int width, int height) {
        super(length, width);
        this.height = height;
    }

    public String toString() {
        return "Box: " + super.toString() + ", Height = " + height;
    }
}
Usufruct answered 4/9, 2024 at 3:12 Comment(1)
Learn to use markdown to format your posts. Nonetheless, answers that contain only code are generally considered to be poor quality. Notice how the other answers contain code and an explanation of what the code does and how it works? Apart from that, the posted question contains code for an Animal class and a Dog class that extends Animal, however your code uses Rectangle and BoxFigure. How do your classes apply to the posted question?Capital

© 2022 - 2025 — McMap. All rights reserved.