7.5. Animal Example¶
In this section, we will work through another inheritance example involving animals.
Test Yourself
Take a moment to look over the UML class diagram below and answer the following questions. If you are unsure, do the best you can and write your thoughts in your notes.
How many inheritance relationships are present in the diagram?
In each inheritance relationship, which class is the parent and which is the child?
Look at each inheritance relationship. Intuitively, does an inheritance relationship seem appropriate?
Test Yourself Solution (open after answering the question above)
There is one inheritance relationship.
DogextendsAnimal. The other relationships (uses) indicate a general dependency, not inheritance.Dogis the child class andAnimalis the parent.When answering this question, ask yourself if the
is-arelationship makes sense (the is-a test). Would you say thatDogis anAnimal? Yes! So, the inheritance relationship is appropriate.Another way to think about it is whether or not it is appropriate for
Dogto inherit thegenusandspeciesattributes and thegetGenusandgetSpeciesmethods. Again, the answer is “yes”!
To work through this example, perform the following steps:
Change into the
animalsubdirectory ofcs1302-inheritance.Inspect the
.javafiles undersrc. In particular, familiarize yourself with the code for theAnimalandDogclasses and compare the code to what you see in the UML diagram.Try the following before looking at the solution:
Test Yourself
Try Modifying
Dog.javaso that theDogconstructor invokes the parent constructor viasuper.Hint: You will need to call the
Animalconstructor from within theDogconstructor using the literal values"Canis"and"Lupus Familiaris"as the genus and species, respectively. This will setup thegenusandspeciesvariable within anyDogobjects that are created.Test Yourself Solution (open after attempting the above)
The parent constructor (in the
Animalclass) takes two parameters:genusandspecies. Since we are invoking theAnimalconstructor on a type of animal where we know the exact genus and species, we can provide the literal values to the parent constructor.super("Canis", "Familiaris");
Warning
Since
genusandspeciesare declared withprivatevisibility, you cannot access them directly (without a getter/setter):super.genus = "Canis"; super.species = "Lupus Familiaris";
Even if the visibility allowed you to write the lines above, you should avoid doing so! There is already code that sets up these variables. It is in the parent constructor. We should use
superinstead of duplicating that initialization code.Compile the
cs1302.animal.Animalandcs1302.animal.Dogclasses, specifyingbinas the default package for compiled code. Since there is a dependency between those two classes, remember to properly specify the class path, as needed, when you compile.Modify the
mainmethod in thecs1302.animal.Driverclass to create someDogobjects, then print out information about them:Dog bulldog = new Dog("Bulldog"); System.out.println(bulldog.getGenus()); System.out.println(bulldog.getSpecies()); System.out.println(bulldog.getBreed()); System.out.println();
Note
Because
Animalvariables are compatible withDogobjects, the datatype of thebulldogvariable could beAnimal. However, if you made this change, you would no longer be able to call thegetBreedmethod since that method is not available inAnimal.Add code in
Driver.javato createDogobjects for the following breeds: poodle, beagle, and pug. CallgetGenus,getSpecies, andgetBreedon each breed as we did in the previous step.Compile the
cs1302.animal.Driverclass, specifyingbinas the default package for compiled code and setting the class path as needed.Run the
cs1302.animal.Driverclass. You should see the following output:Canis Lupus Familiaris Bulldog Canis Lupus Familiaris Poodle Canis Lupus Familiaris Beagle Canis Lupus Familiaris Pug
As you can see from the output, each
Dogobject does containgenusandspecies(inherited fromAnimal) that is set by theDogconstructor using the parameter values that you supplied. Furthermore, you can also see that eachDogobject has the getter methods that it inherited and that they return the values of the inherited variables.
Reminder
If a child constructor does not explicitly call a parent class
constructor via super, then Java will automatically add
a call to super(), i.e., it will attempt to invoke the parent’s
default constructor if it exists. We always recommend explicitly
calling a parent constructor, even if it’s just super(), so that
it’s clear from reading the source code what your intent is.
Here is a video outlining the steps in this section if you got stuck:
7.5.1. Review Question¶
Test Yourself: Writing a Child Constructor
Given the UML Class Diagram below, write the constructor(s) for the child
class(es) in your notes. If the user inputs a negative value for an id,
the constructor(s) should throw an IllegalArgumentException containing
an appropriate message.
Review Question 1 Solution (Open after answering the question above)
The constructor for the
Studentclass should look like this:/** * Initializes the instance variables of a new {@code Student} object. * * @param name the name of the student. * @param age the age of the student. * @param studentId the student's id number. * @throws IllegalArgumentException if the student id number is a negative value. */ public Student(String name, int age, int studentId) { super(name, age); if (studentId < 0) { throw new IllegalArgumentException("The student id number must be nonnegative"); } // if this.studentId = studentId; } // Student
The constructor for the
Professorclass should look almost identical to theStudentconstructor withstudentIdreplaced withemployeeId. Can you think of a way to avoid redundancy by writing a method to check if the id is valid?