7.3. Person Example (Refactoring Existing Code)¶
In our first interactive example, we will work through the code
in the person subdirectory of our starter code. We will leverage
inheritance to eliminate redundant code in the closely related
Employee class.
Here is a UML diagram for the starter code. Notice the redundant
methods and variables between Person and Employee.
In the video below, Dr. Cotterell refactors the code corresponding to the UML class diagram above.
Note
The example code in this section was written without inheritance, so we need to refactor it to eliminate existing redundancies. However, in a real world setting, you would want to design carefully to avoid writing redundant code in the first place!
To get the most out of this section, you will want to follow along
with Dr. Cotterell in the video below. But first, make sure to change
into the person subdirectory of cs1302-inheritance.
https://www.youtube.com/watch?v=V5Y85rfMfPw
Similar to how we use implements in a class declaration to have
a class implement an interface, we use the keyword extends in
the class declaration of a child class to set up the inheritance
relationship. After replicating the steps in the video, an inheritance
relationship is established. The UML diagram below illustrates this
new relationship (note the different arrow head types and compare the
size of the Employee class in this diagram with the diagram
above):
By utilizing the power of inheritance, we were able to remove two redundant instance
variable declarations from Employee along with two redundant method declarations.
These instance variables and methods are now written only once - in Person.
The benefit may seem limited in this small example. However, it is important to consider the
impact of inheritance on a larger software system. For example, in a larger software
system, we could extend this hierarchy to include other people. We could add classes
for Student, Athlete, Professor, etc. and
each of these classes would automatically inherit the instance variables and
methods from Person. So, in general, declaring entities (methods, variables)
in the parent eliminates redundant code across all child classes!
Rapid Fire Review
In the refactored
Employeeclass, which keyword tells Java thatEmployeeshould inherit fromPerson?import
implements
extends
super
Why do we use the
superkeyword in theEmployeeconstructor?To call a method defined in
Employee.To call the constructor of the parent class (
Person)To make the instance variables private instead of public.
To loop over all of the arrays initialized in this class and set their values to
null.
After refactoring, why can’t
Employeedirectly accessnameanddateOfBirthvariables fromPerson?They are declared in a different package
They are declared as private in
PersonEmployeedoesn’t have a constructorJava doesn’t allow inheritance of abstract fields
What happens if you try to access
namedirectly insideEmployeeafter extendingPerson?It works fine since
Employeeinherits it.It causes a compile-time error because
nameis private in Person.It throws a runtime error.
It makes
namenull.
What must be the very first line inside the
Employeeconstructor if you want to call thePersonconstructor?this(id);
Person();
super(…);
extends Person;
In the refactored
Employeeclass, how doesgetName()work if we removed it fromEmployee?Employeecan’t access names anymore.Java automatically generates a new
getNamemethod.EmployeeinheritsgetName()fromPerson.getName()is private inPerson.
Why did Dr. Cotterell keep the
toString()method inEmployeeinstead of removing it?Because Java requires every subclass to override
toString().Because
Persondoes not have atoString()method.To include
Employee-specific fields (idanddateOfHire).To avoid compile-time errors.
Solutions
C
The
extendskeyword establishes inheritance in Java. It tells the compiler that Employee is a child class of Person and should inherit its fields and methods.B
We use
super(...)to call the parent class constructor so that the instance variables inPerson(name,dateOfBirth) are initialized in the class where they are declared. This avoids duplicating initialization code inEmployee.B
Private instance variables are hidden from child classes. Although
Employeeinherits them, it cannot access them directly. Instead, it must use the parent’s public getters or constructors.B
Even though
Employeeinherits the instance variables fromPerson, private visibility prevents direct access. To usename,Employeemust callgetName()or use the constructor.C
The
super(...)call invokes the parent constructor and must appear as the first line in the child constructor. This ensures the parent portion of the object is initialized first.C
Because
Employee extends Person, all public methods fromPerson, likegetName(), are automatically available inEmployee. There’s no need to rewrite them.C
The
toString()method inPersonshows only name and birth date, butEmployeeneeds more detail. Overriding allows thetoString()method inEmployeeto includeidanddateOfHire.