Inheritance and Polymorphism
We can also build classes that inherit attributes and methods from another class. This allows us to build more complex structures in our code, better representing the relationships between real world objects.
Inheritance in UML
As we learned earlier in this chapter, we can represent an inheritance relationship with an open arrow in our UML diagrams, as shown below:
In this diagram, the Student
class inherits from, or is a subclass of, the Person
class.
Inheritance in Java
To show inheritance in Java, we can use the extends
keyword after the class name in our class declaration, listing the parent class that we are inheriting from:
public class Student extends Person{
}
From there, we can quickly implement the code for each attribute and getter method in the new class:
public class Student extends Person{
private int studentID;
private int gradeLevel;
public int getStudentID(){ return this.studentID; }
public int getGradeLevel(){ return this.gradeLevel; }
}
Method Overriding
Since the subclass Student
also includes a definition for the method happyBirthday()
, we say that that method has been overridden in the subclass. We can do this by simply creating the new method in the Student
class, and prefixing it with the @Override
annotation:
public class Student extends Person{
private int studentID;
private int gradeLevel;
public int getStudentID(){ return this.studentID; }
public int getGradeLevel(){ return this.gradeLevel; }
@Override
public void happyBirthday(){
super.happyBirthday();
this.gradeLevel += 1;
}
}
Here, we are using the keyword super
to refer to our parent class. In that way, we can still call the happyBirthday()
method as defined in Person
, but extend it by adding our own code as well.
Inherited Constructors
In addition, we can use the super()
method to call our parent class’s constructor. This must be done as the first line of our subclass’s constructor:
public class Student extends Person{
private int studentID;
private int gradeLevel;
public int getStudentID(){ return this.studentID; }
public int getGradeLevel(){ return this.gradeLevel; }
public Student(String lastName, String firstName, int age, int studentID, int gradeLevel){
super(lastName, firstName, age);
this.studentID = studentID;
this.gradeLevel = gradeLevel;
}
@Override
public void happyBirthday(){
super.happyBirthday();
this.gradeLevel += 1;
}
}
Security
In addition to private
and public
, Java also includes a keyword protected
. This modifier prevents external code from accessing attributes and methods, but will allow any subclasses to access them. In a UML diagram, the protected keyword is denoted by a hash symbol #
in front of the attribute or method.
Polymorphism
Inheritance allows us to make use of polymorphism in our code. Loosely polymorphism allows us to store an instance of a class within the data type of any of its parent classes. By doing so, we can only access the methods and attributes defined by the data type, but any overriden methods will use the implementation from the child class.
Here’s a quick example:
Student steveStudent = new Student("Jones", "Steve", "19", "123456", "13");
Person stevePerson = (Person)steveStudent;
// Can access methods in Person
System.out.println(stevePerson.getFirstName());
// Cannot access methods in Student
System.out.println(stevePerson.getStudentID()); // will not compile
// Can call methods from Person
// This will use the code defined in Student,
// even though it is stored as a Person.
stevePerson.happyBirthday();
System.out.println(steveStudent.getGradeLevel()); // 14
Polymorphism is a very powerful tool in programming, and we’ll use it throughout this course as we develop complex data structures.