Java Inheritance Legacy

I 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 strange behavior happens because the sound in Animal is private and the sound in Tiger is public. But can you explain (and tell me about the relevant parts of JLS) why this is happening?

+7
source share
6 answers

Fields are not polymorphic; methods are polymorphic.

  diego.roar(); 

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

 diego.sound = "Hust hust"; 

Sets the sound value in a Tiger class sound variable

 diego.roar(); 

returns null; because it prints a sound from Animal, which is still zero. The above sound distribution reflects the value of a variable of the Tiger class, not the Animal class.

diego.setSound ("blah");

sets Animal sound to bla

 diego.roar(); 

outputs bla because setSound updates the sound variable of the Animal class with bla .

System.out.println (diego.sound);

displays Hust hust because diego is of type Tiger , and you got access to the sound of the Tiger field, and the fields are not polymorphic.

See the java language 8.3 specification for more details .

+8
source

You can override functions in Java, not variables.

Remove the string public String sound; from Tiger.java

and either:

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

For a more complete explanation, see John Skeet's excellent answer to an almost identical problem yesterday .

+2
source

As already noted, all fields are not susceptible to polymorphism.

My new reason for this is: access to fields is determined statically at compile time, and not dynamically at run time. So here

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

the diego variable is of static type Tiger . Therefore, the compiler will generate access to Tiger.sound . But on the contrary (if Animal.sound not private ):

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

the compiler will generate access to Animal.sound . This may also be caused 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 specify the static type of either the implicit this or diego at this point. Then you also know which of both fields is really available.

+2
source

You must admit that Animal.sound is not the same field as Tiger.sound . In fact, you 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 What you can do in a subclass of Turorial Inheritance .

+1
source

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 private field of the sound member defined in the same Animal class.

sound from Animal not displayed to the Tiger class because it is closed. Thus, you declared a new sound field for the Tiger subclass, but that did not override the source code from Animal . The Animal class still uses its own version of sound because it is the only version it sees. Unlike methods, a field cannot be overridden .

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


Another possible solution would be to use abstract and polymorphism methods:

You do not use the sound method in the base class Animal, you simply declare an abstract method and substitute subclasses in your 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"; } } 

Despite the fact that in Animal there is no implementation (without a body with code) of the sound() ) method, you can still call this method from other methods of this class, for example roar() .

Of course, this approach does not allow you to change the sound of an existing animal object (there is no setter), making the animals unchanged , which may seem uncomfortable at first, but if you think about it for a while, you may find that in many cases you actually do not need to change the state objects that way.

Using immutable objects is actually convenient, because the code is simpler and more secure, because you do not need to think about all the possible states that may occur during program execution.

0
source

the out out (1) and (2) operators refer to the sound of an instance variable of a superclass that is not inherited, since instance / class variables are not inherited in java, but havent set super variable. (3) The super variable is set by calling the inherited method. (4) is set when you performed a direct assignment.

0
source

All Articles