We've been learning about classes using the Student class as an example. So we're on the same page, this is what I have for Student right now:
public class Student {
String name;
int age;
String phone;
public String description() {
return name + " (" + age + ") - " + phone;
}
public boolean isAdult() {
return age >= 18;
}
}
You might have more methods, variables, or constructors; that's fine. For the purpose of this lesson, make sure you have at least the members above.
Let's say we want to expand our student directory app to include Teachers. Teachers have a name, age, phone number, subject and employee ID. They also have a function called getParkingSpot() which looks up their assigned spot in a database and returns it.
Try: Write the Teacher class (the inside of getParkingSpot is not important, just fill it with a default implementation).
You might have something like this:
public class Teacher {
String name;
int age;
String phone;
String employeeID;
public String description() {
return name + " (" + age + ") - " + phone;
}
public String getParkingSpot() {
// Stub to be filled in later
return "default";
}
}
I've also included the description function above.
As you can see, Teacher has a lot in common with Student. As always, when we see repetition in programming, we should have red flags flying. There must be a way to better write this and avoid needless repetition.
Let's think about this deeper. Why do Teacher and Student both have those variables in common. Well, because teachers and students both have names, ages and phone numbers. Why is that? Because both teachers and students are people, and people have names and ages and phone numbers. A teacher is a specialized type of person, who maybe has properties that a normal person might not. Is there some way for us to represent this relationship in Java?
This brings us to the concept of Inheritance. We can make a Person class, and tell the Teacher class to inherit from it.
Let's start by making a Person class with the basic attributes we want:
public class Person {
String name;
int age;
String phone;
public String description() {
return name + " (" + age + ") - " + phone;
}
}
A Person has name, age, phone and the description function.
Now let's go over to our Teacher class. We want Teacher to inherit from Person. In Java, this is done with the extends keyword.
public class Teacher extends Person {
...
}
This tells Teacher that it has all of the members that Person has. Now that this is done, we can go ahead and remove all the duplicate elements:
public class Teacher extends Person {
String employeeID;
public String getParkingSpot() {
// Stub to be filled in later
return "default";
}
}
This cleanly separates the members that belong to all Persons from the ones that are unique to Teacher.
Let's try using this in MainActivity:
Teacher t = new Teacher();
t.name = "Jim";
t.age = 35;
t.phone = "12345678";
t.employeeID = "X1234D";
String spot = t.getParkingSpot();
As you can see, we can use all the members belonging to Teacher, as well as those belonging to Person, because a Teacher is a Person.
Try: Make the Student class inherit from Person as well.
Let's look at some interesting things we can do with this. Say we wanted to make an array of everyone in the school. We might make an ArrayList of Students:
Student s = new Student();
s.name = "Bob";
s.age = 18;
s.phone = "12345678";
Teacher t = new Teacher();
t.name = "Jim";
t.age = 35;
t.phone = "12345678";
t.employeeID = "X1234D";
ArrayList<Student> people = new ArrayList<Student>();
people.add(s);
people.add(t); //ERROR
That last line throws an error. We cannot put a Teacher in an array of Students, for the same reason that we cannot do this:
Student student = t; //ERROR
A Teacher is not a Student, so we cannot mix types like this.
However, both Teacher and Student are Persons! Let's modify the above code:
Student s = new Student();
s.name = "Bob";
s.age = 18;
s.phone = "12345678";
Teacher t = new Teacher();
t.name = "Jim";
t.age = 35;
t.phone = "12345678";
t.employeeID = "X1234D";
ArrayList<Person> people = new ArrayList<Person>();
people.add(s);
people.add(t); // no error
This is because a Teacher _is a _Person, and the following code is completely valid:
Person person = t;
We can now run loops over this array if we want:
for (int i=0; i<2; i++) {
Log.d("MainActivity", people.get(i).description());
}
It doesn't matter whether people.get(i) is actually a Student or Teacher. It's a Person, and a Person has a description().
If we want to access Teacher specific members, you might encounter an error:
people.get(1).employeeID;
This makes sense. A Person does not have an employeeID. If this code did not throw an error, we might try to access the employeeID of a plain Person, or a Student. To access Teacher members, we have to cast it back into a Teacher.
Teacher teacher = (Teacher) people.get(1);
String id = teacher.employeeID;
However, be very careful when casting. When you are performing a cast, you're basically telling the compiler to not worry about it, because you know for a fact that the conversion works. If the above code was this:
Teacher teacher= (Teacher) people.get(0);
String id = teacher.employeeID;
You would get a crash. There will be no compiler errors, because a Person could be converted to a Teacher. But in this case, the object in that slot is a Student.
To perform type conversions safely, use the instanceof keyword:
if (people.get(0) instanceof Teacher) {
Teacher teacher = (Teacher) people.get(0);
String id = teacher.employeeID;
}
This makes sure there won't be a crash. Keep in mind though, that using instanceof often might be a sign that your code is poorly organized in some way. It's not wrong to use it, but it is definitely a code smell: https://en.wikipedia.org/wiki/Code\_smell.