views:

113

answers:

7

Inheritance.java

public class InheritanceExample {
  static public void main(String[] args){
    Cat c = new Cat();
    System.out.println(c.speak());

    Dog d = new Dog();
    System.out.println(d.speak());
  }
}

Animal.java

public class Animal {
  protected String sound;
  public String speak(){
    return sound;
  }
}

Cat.java

public class Cat extends Animal {
  protected String sound = "meow";
}

Dog.java

public class Dog extends Animal {
  protected String sound = "woof";
}

Output:

null
null

My animals cannot speak. So sad.

+9  A: 

Fields aren't polymorphic. You've declared three entirely distinct fields... the ones in Cat and Dog shadow or hide the one in Animal.

The simplest (but not necessarily best) way of getting your current code is to remove sound from Cat and Dog, and set the value of the inherited sound field in the constructor for Cat and Dog.

A better approach would be to make Animal abstract, and give it a protected constructor which takes the sound... the constructors of Cat and Dog would then call super("meow") and super("woof") respectively:

public abstract class Animal {
    private final String sound;

    protected Animal(String sound) {
        this.sound = sound;
    }

    public String speak(){
        return sound;
    }
}

public class Cat extends Animal {
    public Cat() {
        super("meow");
    }
}

public class Dog extends Animal {
    public Dog() {
        super("woof");
    }
}
Jon Skeet
Well what's the **best** way? (You made me ask).
macek
@macek: I was getting there... just typing as quickly as I could :)
Jon Skeet
@Jon Skeet, what if I have several fields to populate. Surely I wouldn't want a rigid constructor that takes a variable for each field... right?
macek
@Jon: Phew! You made it within that 5-minute window! (Once again.)
BoltClock
@macek: That really depends. Maybe you'll set everything in the constructor, or maybe it's appropriate to have `setFoo` methods... it partly depends on whether you're aiming for immutability. Another option is overriding methods in the subclasses. It all depends :)
Jon Skeet
+4  A: 

You cannot override class fields, only methods. The sound field in your Dog and Cat classes is actually hiding the sound field in the Animal superclass.

You can, however, access superclass fields from subclasses, so you could do something like this:

public class Dog extends Animal {
  public Dog() {
    sound = "woof";
  }
}

public class Cat extends Animal {
  public Cat() {
    sound = "meow";
  }
}

Or, you can make the Animal class abstract, and declare the speak method abstract too, then define it in subclasses:

public abstract class Animal {
  public abstract String speak();
}

public class Dog extends Animal {
  public String speak {
    return "woof";
  }
}

public class Cat extends Animal {
  public String speak {
    return "meow";
  }
}
Grodriguez
A: 

You're hiding fields. The sound in Animal is not the same String as the sound in Cat.

One possible solution is to create a constructor and there simply say

super.sound = "meow";

instead of in the class body saying

protected String sound = "meow";

to set the field.

Joonas Pulakka
A: 

A method will look in its own class' namespace to resolve fields. While methods defined in sub-classes can look up the hierarchy to resolve fields, the same is not true for classes defined higher up in the hierarchy, i.e., super-classes won't look down the hierarchy to resolve fields [and their values]

anirvan
A: 

You're shadowing the field inherited from Animal. You have a few options, but the prettiest way of doing it is passing the sound in the constructor:

public class Animal {
  private final String sound;
  protected Animal(String sound){
    if (sound == null)
      throw new NullPointerException("sound");
    this.sound = sound;
  }
  public String speak(){
    return sound;
  }
}

public class Cat extends Animal {
  public Cat(){ super("meow"); }
}

public class Dog extends Animal {
  public Dog(){ super("woof"); }
}

This way, you can make sure that an Animal always has a valid sound, right from construction.

gustafc
A: 

The Java(TM) way is to declare the protected String getSound() in Animal.java and implement it in the subclasses.

Tassos Bassoukos
A: 

You didn't allow your animals to speak! you should do like this :

Cat.java:

public class Cat extends Animal {

// protected String sound = "meow";

public Cat(){
    this.sound = "cat";
}

}

Dog.java:

public class Dog extends Animal {

// protected String sound = "woof";

public Dog(){
    this.sound = "dog";
}

}

just because there are two members "sound" in Cat or Dog,and the one inherited from Animal is hidden without value(so it prints null);another is special to Cat or Dog,which is assigned a value. So you should use the pointer 'this' to quote the original member 'sound'.

shenju