Week 7 Inheritance Written by Alexandros Evangelidis. Adapted from Joseph Gardiner 10 November 2015 1 This Week By the end of the tutorial, we should have covered: Inheritance What is inheritance? How does it work? Why do we need it? When do we use it? Abstract Classes 2 Inheritance One of the key features in Java and many object-oriented programming languages is the idea of inheritance. 2.1 What is inheritance? Inheritance is the ability of classes to take, or inherit, certain traits from a parent or superclass, without the need to write them explicitly. It s similar to the way a child can have a parent s eyes or nose, except in Java we as programmers can specify what is and isn t passed on. In general, child or subclasses will inherit all methods and global variables in the parent class, except those which are declared private. The key thing that subclasses do not inherit is the constructor - you need to write a new one in each of your subclasses. You have all seen evidence of inheritance already, in some form or another. In fact, every single Java class ever written is a subclass of Object. Another good example of inheritance would be the Exceptions from last week - ArrayIndexOutOfBoundsException and all other exceptions inherit from the Exception class. 2.2 How does it work? To specify a parent class in Java, we need to use the extends keyword. We use this as follows: public class Example extends Parent In this case, the Example class would inherit all methods and variables from the Parent class. Note that Java only allows one parent per class, so you cannot write, say: public class DoesntWork extends Parent, OtherParent 1
Just like in the real world, however, there is nothing to stop a class from being parent to multiple subclasses. Again, Exception is a good example of this - it has 56 direct subclasses! Included in these are IOException and RuntimeException which have been mentioned before. Similarly, although a class can have only one parent, the parent class can have its own parent, and that can have its own, and so forth. This allows you to create a hierarchy of classes in Java, starting from the general and branching down to the specific. Once again returning to Exceptions, you have seen ArrayIndexOutOfBoundsException which has a fairly lengthy ancestry: java.lang.object extended by java.lang.throwable extended by java.lang.exception extended by java.lang.runtimeexception extended by java.lang.indexoutofboundsexception extended by java.lang.arrayindexoutofboundsexception So ArrayIndexOutOfBoundsException inherits from IndexOutOfBoundsException, which inherits from RuntimeException, which inherits from Exception... and so on. Because each class inherits all methods and fields from its parent, it can then pass these on to its own subclasses, meaning the children inherit from the entire family tree. 2.3 Why do we need it? Inheritance offers several benefits to programmers. First is the ease with which we can reuse code if you ve written a method in one class that you need in another, you can inherit rather than copy and paste the code across. This also has the advantage of keeping code in one place if the inherited method had a bug in it, you would only have to fix it in the parent class, and the child class would inherit the fix automatically. Inheriting fields and methods from superclasses also allows you to focus only on the unique aspects of each class - by keeping general fields and methods in parent classes, you only have to write the specific methods and fields in each subclass, which makes them less cluttered and easier to work with. 3 Abstract Classes Before you can use inheritance, you need to have something to inherit from. This is the superclass. It may be abstract, that is, you cannot create objects of this type directly, but only via its subclasses; or concrete, that is, objects of this type can co-exist with objects of the subclasses. A superclass is the blueprint for all its inherited classes. For example, consider the example of a user of a system: public abstract class User { private String firstname; private String surname; private String username; public User(String firstname, String surname, String username) { super(); this.firstname = firstname; this.surname = surname; this.username = username; 2
//getters and setters public String tostring(){ return "[" + firstname + "," + surname + "," + username + "]"; The class is made abstract by including the word abstract in the class definition. Below this, the class is as normal, with getters, setters and a tostring() method. 4 Extending Classes Imaging that we extend the above example to a system that represents the School of Computer Sciences network. Users can either be a staff member, or a student, and each student can either be an MSc student of a PhD student. The class structure is as follows: User Lecturer Student MScStudent PhDStudent A lecturer has two extra variables over a User: a salary and a list of the modules that they teach. A Student has one extra variable: the degree programme. For example, the Lecturer class is established as: public class Lecturer extends User { ArrayList<String> modules; int salary; public Lecturer(String firstname, String surname, String username, int salary, ArrayList<String> modules) { super(firstname, surname, username); this.salary = salary; this.modules = modules; here, we call the constructor of the User class using super(firstname, surname, username). This allows us to fill these variables in the superclass. We then set the local variables from the constructor for salary and modules. 3
The Student class extends User, but it is also the superclass to MScStudent and PhDStudent, therefore it needs to extend user. We declare the Student class as abstract, since we do not want that objects of the class are directly created (which would mean having student objects for which we don t know whether they should represent MSc or PhD students). public abstract class Student extends User The MScStudent and PhD student classes both extend student (and also through inheritance User). An MScStudent also has a module list to represent the modules they are taking, while a PhD student has a supervisor. 5 Using Inheritance We can now use each of these classes in the following way: Lecturer manfred = new Lecturer("Manfred", "Kerber", "mmk", 50000, new ArrayList<String>()); manfred.addmodule("software Workshop 1"); System.out.println(manfred); MScStudent smith = new MScStudent("John", "Smith", "jxs123", new ArrayList<String>()); smith.addmodule("software Workshop 1"); smith.addmodule("databases"); smith.addmodule("computer Security"); System.out.println(smith); PhDStudent jack = new PhDStudent("Jack", "Jones", "jxj652", "George Naylor "); System.out.println(joe); In this example, we have made one object of each of the three different subclasses. Each of the subclasses has its own tostring() method, which overrides the tostring method in the abstract class. For example, the tostring() in the Lecturer class is: @Override public String tostring() { String out = "[" + getfirstname() + "," + getsurname() + "," + getusername() + "," + salary + ", Modules:("; //Print modules for (int i = 0; i < modules.size(); i++) { out = out + modules.get(i) + ","; out = out + ")]"; return out; Notice in here that above the method name there is the @Override notation. This indicates that this method is overriding the method in the superclass. Also note the use of getfirstname(). Since this method does not exist in the subclass it is taken from the superclass. Since the corresponding variable firstname is private in the superclass we need to use the getter in order to access it. 4
Using Lists Assume we want to have a list containing all users in the system. We have already defined three users, one of each type. Surely that means we have to have one list for each type? No! Inheritance provides us with the ability to group different objects that share the same super class. Look at the example below: ArrayList<User> users = new ArrayList<User>(); users.add(manfred); users.add(smith); users.add(joe); for (int i = 0; i < users.size(); i++) { System.out.println(users.get(i)); In this example, we make an ArrayList of type User, and add each of our three objects to it. As our subclasses extend User, we are able to do this. If we then run this code, the output is as follows: [Manfred,Kerber,mmk,50000, Modules:(Software Workshop 1,)] [John,Smith,jxs123,MSc Java, Modules:(Software Workshop 1,Databases,Computer Security,)] [Jack,Jones,jxj652,PhD Computer Science, Supervisor:George Naylor] Notice that each element in the list has caused a different formatted string. This is because each of the subclasses has a tostring() method which overrides that of User, which is what is called when System.out.println() is called on them. 6 Some Other Points An important point about inheritance is that, while methods can be overridden, variables are not. They should be private in the first place and accessed only via getters and setters. (A change to a variable which has a copy in a subclass would not result in a change to the same variable in the superclass.) 5