Search code examples
javaconstructorparent-childdefault-constructor

NOT wanting to call the default constructor from child class


I am working on an animal class which needs a default constructor. I also need to extend the animal class with specific animals as well, with their own names and sounds.

My Animal class is as follows:

public class Animal {

public String name;
public String sound;

public Animal() {
    this.name = "animal";
    this.sound = "an animal makes a sound based on what animal that it is";
    System.out.println("I am an animal");
}
        
public void sound() {
    System.out.println("The " + name + " says " + sound);
}

After I wrote the other animal classes such as Sheep, pig and cow, I wrote a test class to verify if it was working. They're carbon copies basically but here is the first one I wrote, the cow class:

public class Cow extends Animal {
public String name = "Cow";
public String sound = "Moo";


public Cow() {
    System.out.println("I am a cow");
}
 

public void sound () {
    System.out.println("The " + name + " says " + sound);
}
    

}

My issue is that after making new "cows", or any of the other animals they all start out by saying "I am an animal" which is from the default constructor within the animal class and not needed. How can I avoid this? I was under the impression the default constructor would only be called when creating a new animal via Animal animal1 = new Animal(); but clearly, that is not the case.

I've been reading my textbook, looking over YouTube as well as here on Stack but to no avail.

Thought maybe I had to override something but I am not sure.

Here is my Test class and output:

public class Test {

public static void main(String[] args) {
    Animal animal = new Animal();
    animal.sound();
    Cow cow = new Cow();
    cow.sound();
                
}

}

Output:

I am an animal
The animal says an animal makes a sound based on what animal that it is
I am an animal
I am a cow
The Cow says Moo

Because I am actively trying to learn java I would very much appreciate being guided towards an answer, as well as an explanation to help me understand why this is happening.


Solution

  • The question why the superclass constructor is called, even without explicit super(); call is answered here. One of the constructors of the superclass must always be called. If the superclass has a (possibly implicit) default constructor then the super(); call from the subclass can be omitted, but the constructor is called implicitly. Your Cow constructor is the same as this one:

    public Cow() {
        // Calls the `Animal()` superclass constructor
        super();
        System.out.println("I am a cow");
    }
    

    In your specific situation it might make sense to add an additional Animal constructor which takes the name and sound values as parameters and initializes the fields. If you still need a default Animal() constructor, you can have that delegate to the other constructor:

    class Animal {
        public String name;
        public String sound;
    
        public Animal(String name, String sound) {
            // `this.name` refers to the field, `name` refers to the parameter
            this.name = name;
            this.sound = sound;
        }
    
        public Animal() {
            // Uses `this(...)` to call the other constructor
            this(
                "animal",
                "an animal makes a sound based on what animal that it is"
            );
            System.out.println("I am an animal");
        }
    
        ...
    }
    

    Note: If you don't expect these fields to be reassigned, then you should make them final to protect against bugs and for thread-safety improvements.

    Your subclasses can then call the Animal(String, String) constructor:

    public class Cow extends Animal {
        public Cow() {
            // Calls the `Animal(String, String)` superclass constructor
            super("Cow", "Moo");
        }
    }
    

    This does not declare additional name and sound fields because the ones from Animal are inherited and accessible to Cow. Your current code for Cow actually introduces additional fields which hide the ones from Animal, see this answer.