2
public class Animal{

    int n = 5;

    public static void main(String[] args) {

        Animal a = new Animal();
        Animal ah = new Horse();
        Horse h = new Horse(); 

        System.out.println(h.n); // prints 7
        System.out.println(ah.n); // prints 5
        h = (Horse) ah;
        System.out.println(h.n); // prints 7

    }
}

class Horse extends Animal{

    int n = 7;

}

My question:

Why does h.n still print 7 after h = (Horse) ah? After the assignment it should point to the same object that ah points and the n field points to 5?

Bogdan Vasilescu
  • 407
  • 4
  • 22
  • I am new to the forum. I had not put any comments. Somebody changed it. I know it prints 7, 5, 7. Then I saw that it got changed by someone. Then I saw your comment. I approved your edit. But I hadn't approved the first one? How did it get changed? – javacurious Feb 06 '11 at 01:45
  • I know it wasn't you that added those comments. Thanks for approving my edit. People with more reputation than myself (I think 2000) can edit without your approval, so that's what happened earlier. In future it is best that you provide the output you are seeing. Thanks! – mgiuca Feb 06 '11 at 01:48
  • Also, if you think my answer answers the question fully, can you tick the "accepted answer" checkbox? Cheers. – mgiuca Feb 06 '11 at 01:51
  • also by saying static type you are referring to the reference variable type, right? – javacurious Feb 06 '11 at 01:54
  • Yes, though I haven't heard the terminology "reference variable type". But yes, the "static type" is the type of the *variable* which holds the object. As opposed to the "dynamic type" which is the type of the actual object itself. Upcasting and downcasting changes the static type, but not the dynamic type. So `ah` has static type `Animal` but dynamic type `Horse`. – mgiuca Feb 06 '11 at 01:57

2 Answers2

8

First, let's call the field n of the class Animal "Animal.n" to avoid confusion.

Fields, unlike methods, are not subject to overriding. So in your Horse class, where you may think you are overriding the value of Animal.n with 7, you are actually declaring a new variable called n (let's call it Horse.n to avoid confusion).

So really, you have a class called Horse with two fields: Animal.n and Horse.n. Which field you get when you say "n" depends upon the static type of the variable at the time.

When you have an object whose type is Horse, but upcast to an Animal, the n field refers to Animal.n, and has a value of "5". Hence ah.n is "5".

When you have the same object, downcast again to a Horse, the n field refers to Horse.n, and has a value of "7". Hence h.n is "7".

To clarify: Indeed, h does point to the same object that ah points to -- downcasting does not change what object is being pointed at. However, the static type does affect which field of the object is being requested.

mgiuca
  • 20,958
  • 7
  • 54
  • 70
  • 2
    To discuss *why* fields are not subject to overriding: what is essential about overriding of methods is that the same interface is used but has a different implementation. Since fields do not have an implementation, only a type and a name, overriding wouldn't do anything. What you probably want here is the Horse *constructor* to assign `a = 7`, rather than declaring a new int. That way, the Horse would only have one field called `a`, which is unambiguously set to 7. – mgiuca Feb 06 '11 at 01:42
  • Nice explanation for a strange problem. Let's better keep all fields private. :D – maaartinus Feb 06 '11 at 02:42
  • @maaartinus Indeed! If all fields were private this could never arise. But I do think there is some benefit to having protected fields (ideally not, but in practice it can save a lot of time and still have most of the benefit of private fields), in which case this question is still relevant. – mgiuca Feb 06 '11 at 23:52
  • @mgiuca: What time can it safe? All accessors can get inlined, so you mean typing time, right? I'd prefer to use lombok, to make the accessors for me. – maaartinus Feb 07 '11 at 00:13
  • @maaartinus Yes I meant programmer time, not execution speed. I personally dislike tools which auto-generate code. It's more than just typing time -- there's the cost of maintaining a larger code base with more methods, as well as the cost of typing `a.setX()` instead of `a.x =` all the time, and the readability that comes with it. For public fields, I think the tradeoff is worth it (much better to put in the effort for a robust interface), but for protected, I don't think the payoff is there, unless you are writing a library package. – mgiuca Feb 07 '11 at 00:16
  • Agreed, just a note: Lombok doesn't put anything in the source code, it's Java agent hacked into the compiler. – maaartinus Feb 07 '11 at 00:42
  • @maaartinus Wow, I just read the Lombok docs. Looks really handy, and doesn't require an IDE or custom code generation tool. Thanks for clarifying that or I wouldn't have bothered to look. – mgiuca Feb 07 '11 at 01:46
0

Every object you create (called an instance) in Java points to the class from which it was created. This is the true type of the object and doesn't change when you cast a reference to it. Similarly, every variable that references an object has a declared type. This is how the compiler will treat the object and determines what operations are allowed.

However, these two types are only loosely coupled. Casting object references changes only the declared type. It doesn't affect how the object behaves--only how the compiler treats it.

David Harkness
  • 35,992
  • 10
  • 112
  • 134