8

I have created the following puzzle for inheritance in Java:

Animal.java

public class Animal {
    private String sound;

    public void roar() {
        System.out.println(sound);
    }

    public void setSound(String sound) {
        this.sound = sound;
    }
}  

Tiger.java

public class Tiger extends Animal {
    public String sound;

    public Tiger() {
        sound = "ROAR";
    }
}

Jungle.java

public class Jungle {
    public static void main(String[] args) {
        Tiger diego = new Tiger();

        diego.roar();
        diego.sound = "Hust hust";
        diego.roar();
        diego.setSound("bla");
        diego.roar();
        System.out.println(diego.sound);
    }
}

Output:

null
null
bla
Hust hust

I guess this weird behaviour is taking place, because sound in Animal is private while sound in Tiger is public. But can you explain (and tell me the relevant parts of the JLS) why this happens?

Martin Thoma
  • 124,992
  • 159
  • 614
  • 958
  • 1
    You might find this useful: http://stackoverflow.com/questions/685300/is-there-a-way-to-override-class-variables-in-java – Colin D Dec 07 '12 at 20:49
  • 2
    Section [8.3](http://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.3) in the JLS talks about field hiding. – Jacob Schoen Dec 07 '12 at 20:52

6 Answers6

10

Fields are not polymorphic, methods are polymorphic.

 diego.roar();

calls roar() method in Animal and prints sound from Animal.

diego.sound = "Hust hust";

Sets sound value in Tiger class sound variable

diego.roar();

returns null; because prints sound from Animal, which is still null. Above sound assignment reflects on Tiger class variable, not Animal class.

diego.setSound("bla");

sets Animal sound to bla

diego.roar();

prints bla because setSound update sound variable of Animal class with bla.

System.out.println(diego.sound);

prints Hust hust due to the fact that diego is of type Tiger and you have accessed field sound of Tiger and fields are not polymorphic.

Please refer java language specification 8.3 for more details.

kosa
  • 65,990
  • 13
  • 130
  • 167
  • I'm not sure if I really understood this: "update sound variable of Animal class": So my Object of the type Tiger has variables of the type Tiger and Animal? How can/do I access the variables of the Type Animal inside of Tiger?: Ah, just found it: super.Sound – Martin Thoma Jan 14 '13 at 19:33
2

You can override functions in Java, not variables.

Remove the line public String sound; from Tiger.java

and either:

  • Declare String sound as protected or public in Animal.java, or
  • Define a setSound() function for Animal.java for controlled access to member variables (i.e. sound)

For a fuller explanation, see Jon Skeet's excellent answer to an almost identical problem yesterday.

Community
  • 1
  • 1
sampson-chen
  • 45,805
  • 12
  • 84
  • 81
2

As other have already noted: Fields are not subject to polymorphism.

My new twist to this is: Access to fields is decided statically at compile time, not dynamically at runtime. So here

Tiger diego = new Tiger();
diego.sound = "Hust hust";

the variable diego has the static type Tiger. So the compiler will generate an access to Tiger.sound. But in contrast (if Animal.sound would not be private) :

Animal diego = new Tiger();
diego.sound = "Hust hust";

the compiler will generate an access to Animal.sound. This can be also forced by casting:

Tiger diego = new Tiger();
((Animal)diego).sound = "Hust hust";

With this in mind you can go through your puzzle and for each access to any sound field you can tell the static type either of the implicit this or of diego at that point. Then you also know which of both fields is actually accessed.

A.H.
  • 63,967
  • 15
  • 92
  • 126
1

You have to recognize the Animal.sound is not the same field as Tiger.sound. You in fact have two different fields, that can have two different values, and are set in two different ways.

Animal.setSound() updates the value of Animal.sound, does not update the value of Tiger.sound.

diego.sound = "Hust hust" updates the value of Tiger.sound, not the value of Animal.sound.

See the section What you can do in a subclass in the Inheritance Turorial.

Jacob Schoen
  • 14,034
  • 15
  • 82
  • 102
romacafe
  • 3,098
  • 2
  • 23
  • 27
  • Although this answer is correct, exctly what I was expecting for this question. But also, I would be more interested if you add some more technical terms into it, as to why is this behaviour? Why `diego.sound` refers to `Tiger.sound`, so that it can clarify the doubts of OP. – Rohit Jain Dec 07 '12 at 21:00
0

Change the Tiger class to:

public class Tiger extends Animal {

    public Tiger() {
        setSound("ROAR");
    }
}

The problem is that the roar() method defined in Animal uses the member private field sound defined in the very same Animal class.

The sound from Animal isn't visible to the Tiger class, because it's private. So you declared a new sound field for the Tiger subclass, but that didn't override the original one from Animal. The Animal class still uses it's own version of sound, because it's the only version it sees. Unlike methods, field can't be overriden.

One solution is to use getter/setter methods declared in the base class (Animal) for all access to properties, even from the subclasses.


Another possible solution would be using abstract methods and polymorphism:

You don't implement sound method in the Animal base class, you just declare an abstract method and force subclasses to provide their own implementations:

public abstract class Animal {
    public void roar() {
        System.out.println(sound());
    }

    public abstract String sound();
}

public class Tiger extends Animal {
    public String sound() {
        return "ROAR";
    }
}

public class Dog extends Animal {
    public String sound() {
        return "HOOF HOOF";
    }
}

Even though there is no implementation (no body with code) of the sound() method in Animal, it is still possible to call that method from other methods of this class, such as roar().

Of course, this approach makes you unable to change the sound of an existing animal object (there is no setter), making animals immutable, which might seem inconvenient at first, but if you think about it for a while, you might find out that in many cases, you do not actually need to change state of the objects in this manner.

Using immutable objects is actually convenient, because the code is more simple and secure, because you don't have to think about all the possible states that can possible occur during the execution of the program.

Natix
  • 14,017
  • 7
  • 54
  • 69
0

system out statements (1) and (2) are referring to the instance variable sound of superclass which is not inherited as instance/class variables are not inherited in java and you havent set super variable. (3), the super variable is set via calling the inherited method.(4) is set when you did a direct assignment.

CodeDreamer
  • 444
  • 2
  • 8
  • @CodeDreamer I believe instance/class variables can be inherited, depending on the modifier(public, private, protected, default) used. In this particular instance you are correct, but it reads like you mean in all cases. See [Controlling Access to Member of Class](http://docs.oracle.com/javase/tutorial/java/javaOO/accesscontrol.html). – Jacob Schoen Dec 07 '12 at 21:02
  • Hi I mean to say that polymorphism are only for methods and not for fields. You are referring to the visibility of the fields. That is correct. But what I am referring to is what happens when both the super class and subclass have same variables. Then there is no polymorphism, rather the variable referred is that of the Compile time class and not runtime class. That was my point as well. – CodeDreamer Dec 07 '12 at 21:39