开发者

How does instance variable invocation work when you do A thing = new B(); where B is a subclass of A?

This is probably answered somewhere, but I have no idea what to search for. Imagine you have the following...

The superclass, Animal.java

public class Animal {
  public String noise = "squeak";
  public String toString() { return noise; }
}

The subclass, Lion.java

public class Lion extends Animal {
  public String noise = "ROAR!!";
  public String toString() { return noise; }
}

The main class, Runner.java

public class Runner {
  public static void main(String[] args) {
    Animal a = new Animal();
    System.out.println(a);
    System.out.println(a.noise);
    Lion b = new Lion();
    System.out.println(b);
    System.out.println(b.noise);
    Animal c = new Lion();
    System.out.println(c);
    System.out.println(c.noise);
  }
}

The output is:

squeak
squeak
ROAR!!
ROAR!!
ROAR!!
squeak

Why does c.noise return squeak? What is the difference between instance method invocation and instance variables that one returns what you'd expect, and the othe开发者_StackOverflowr does not? Why does Java do this?

Thanks


Short answer:

You can override methods, but it's not possible to override fields.

Long answer:

Each class sees the methods and fields of it's own and of it's parents (except for private methods). If the child delcares a method, whose name is the same, as the name of the method in his parent class, this method becomes overridden - if this method is somehow invoked on the child instance (even from the one of the parent's methods), the brand new method will be used instead of the parent's one. Child may still call the original method of his last parent via super.method(...) call.

But the story is different when we come to the fields. If the child declares a new field, that is named exactly as the field in parent class, it will simply hide the parent's field without overriding, just like the local variable hides global one. So the child methods will simply see the child's fields, but the parent's method will continue to see parent's field, and child's field will not be visible by any means from the parent class - that's what you've got.

Child can access the field of it's parent via ((Parent)this).field.


Longer answer:

So really the way you'd do this is define Lion thus:

public class Lion extends Animal {
  public Lion() {
     noise = "ROAR!!";
  }
}

So now for Lion instances the noise member variable of Animal has been updated to ROAR!!

Of course you'd (almost) never actually have a public mutable member on a class like that in the wild.


You can not override fields, new declaration of noise in Lion hides parent's noise attribute. do like this:

public class Lion extends Animal {

//    public String noise = "ROAR!!";  // <---- Remove this line

    public Lion() {
        noise = "ROAR";
    }

    public String toString() {
        return noise;
    }
}


All non-static methods in java are by default "virtual functions". Unless they're marked as final (which makes the method not overridable). Java uses a virtual method table to call the correct object's method.


This is because Java talks only about method overriding. Member variables can only be shadowed in child class. So when you say c.noise it actually refers to the string variable in parent class as the c if reference type Animal.


The topics you are interested in are Dynamic Dispatch and Virtual Method Table. Basically, by design, Java allows for methods to be overridden (assuming they are non-final) and at run time the JVM will execute the appropriate implementation. This polymorphic attribute is only afforded to methods. Good OO design would dictate the fields be encapsulated anyway.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜