8966474.doc
FBE – Computer Science Dept
Mekelle University Faculty of Business & Economics Computer Science Department Comp 262: Internet Programming with Java Handout 5 – Classes and Inheritance
Reference: You may find it useful to look at the Java tutorial, taken from the Sun Java website, on the intranet – browse to Computers, Course Materials and look under the heading for this course. 1 Overview This handout covers in greater depth the OO concept of inheritance and how it is used in Java. Abstract classes and interfaces, and their uses, are discussed. 2 Extending a class (inheritance) Code Example: Demos/People Inheritance is one of the key principles of OO programming. It allows a class to get, or reuse, the data members and methods of another class. 2.1 Extends Keyword In Java, the keyword used to indicate inheritance is extends. For example, to declare a class named Employee that inherits from a class named Person: class Employee extends Person
Note that this is like using the colon (:) in C++. When class B extends class A, class B automatically inherits all fields and methods from class A. This includes instance and class fields and methods. Class A is called a super class (also known as a base or parent class). Class B is the subclass (also known as a derived or child class). A subclass automatically inherits data and methods from its super class. These are as follows: • •
Super class members declared as public or protected – this includes data (variables) and methods, both class and instance. Super class members declared with no access specifier as long as the subclass is in the same package as the superclass, including both class and instance (a package is a group of related classes grouped together using the package directive – we will discuss packages further at a later stage)
A class that is declared as final cannot be extended. A final class should be used where the class defines functionality that should not be changed in the program or any programs using it. For example: Page 1 of 12
8966474.doc
FBE – Computer Science Dept
final class someClass { }
2.2 Hiding Member Variables If the subclass declares a variable with the same name as a variable in the super class, we say that the subclass hides the super class variable. For example: class SuperClass { int x; } class SubClass extends SuperClass { float x; }
The float variable x hides the int variable x. If x is accessed in the class SubClass, it is accessing the float variable. However, the int variable x can be accessed using the super keyword. Super refers to the super class. Another keyword, this can be used to access the x variable in the subclass – this is the same as just accessing x, but it makes it clear which x is being accessed. x //refers to the float variable x this.x //refers to the float variable x super.x //refers to the int variable x
In practice, it is rare to hide a super class variable – it is more likely that the variable in the subclass represents a different data member of the class, so it should have a different name. 2.3 Overriding Super Class Methods If the subclass declares a method with the same name, return type and parameter list (this is also known as the signature of the method) as a method in the super class, the subclass does not inherit the method from the super class. We say that the subclass overrides the method. When the method is invoked for objects of that class, the interpreter calls the new definition of the method, not the super class's definition of the method. Note that if the subclass declares a method with the same name but with a different parameter list, then it is not overriding the method in the super class. It is overloading the method. An overloaded method can also appear in the same class i.e. two methods with the same name but with different parameter lists can appear in the same class. At runtime, the interpreter determines which method to call based on the parameter list. The keyword super can be used to invoke the method of the super class. This is often used because the overriding method in the subclass is simple adding more functionality to the method in the super class. Consider the following classes, showing a simple initial implementation of the Person and Employee classes (as depicted in Figure 5, on page 7 of Handout 1). The getDetails() method of the Person super class is overridden in the Employee class. It makes a call to the super class method to avoid duplicating the code to get the name, address and date of birth instance variable values. Page 2 of 12
8966474.doc
FBE – Computer Science Dept
import java.util.Date; class Person { protected String name; protected String address; protected Date dateOfBirth; //other class code omitted /** Return a string showing the details for this Person object */ public String getDetails () { } }
return name + "\n" + address + "\n" + dateOfBirth;
public class Employee extends Person { protected long salary; protected String department; protected String rank; //other class code omitted /** Return a string showing the details for this Employee object. First call the getDetails() method in the super class, Person, to get the details from that class.
*/
public String getDetails () { return super.getDetails() + "\nSalary: " + salary + "\nDepartment: " + department + "\nRank: " + rank; } }
The subclass method that overrides a method in the super class must have the same return type – if it does not, the compiler will report an error. This makes sense, because if the overriding method needs to return a different data type, then it is likely that the method is actually performing a different function, so it should have a different name. There are some cases where a method cannot be overridden. • A subclass cannot override a method that is declared as final in the super class. The final keyword means that the method definition is final and cannot be changed. Using the final keyword allows the compiler to make some optimisations when invoking class methods. A method that carries out functionality that does not change should be declared final. • A subclass cannot override a method that is declared as static in the super class. Remember that static methods are class methods i.e. they are accessed using the class name, not an instance/object name. They can be hidden, in the Page 3 of 12
8966474.doc
FBE – Computer Science Dept
same way that instance variables can be hidden – by declaring a static method in the subclass, with the same name and signature as the super class method. 3 Access Modifiers We have seen various access modifiers being used in the classes we have worked with so far. It is important to be aware of the meanings of these modifiers. As mentioned above, they impact what a subclass can inherit from super classes. Public A public variable or method is accessible to all other classes and subclasses. Package Package access is also known as ‘friendly’ access. A class can be assigned to be part of a package. Usually, a group of related classes are assigned to the same package. In Java itself, for example, all IO classes are in the java.io package. If no access modifier is specified for a variable or method, it is accessible only to other classes and subclasses that are in the same package. Protected A protected variable or method is accessible to classes and subclasses in the same package and also to subclasses in other packages. The visibility level of a protected member is between the levels of public and package access. Private A private variable or method is accessible only within the class in which it is declared. It is not accessible to subclasses. In other words, a private variable or method is not inherited by subclasses. A class itself can be declared as public, but cannot be declared as private or protected. If a class has no access modifier specified, it is accessible only in its own package. A class that is declared public is accessible anywhere its package is accessible. The visibility of class members for the different access modifiers are summarised in the table below. Access modifier Accessible to Same class Subclass in same package Other classes in same package Subclass in other package Non-subclass in other package
Public Yes Yes
Protected Yes Yes
Package (friendly) Yes Yes
Private Yes No
Yes
Yes
Yes
No
Yes
Yes
No
No
Yes
No
No
No
The programmer should use the access modifiers to protect the data inside a class, thus applying the OO concept of data encapsulation. Guidelines for deciding what access modifiers to use are as follows:
Page 4 of 12
8966474.doc
•
•
• •
FBE – Computer Science Dept
Use public only for methods and constants (not variables) that form part of the public API1 (Application Programming Interface) of the class. If there are member variables that are important or frequently used, they can be public but it is more common and good practice to make all fields non-public and encapsulate them with public accessor methods (e.g. public String getName(), public String getAddress()). Public members should be documented so that the API informs other programmers about them. Remember that if changes are made to anything that is part of the API of a program, they can potentially break code that relies on the API. Use protected for variables and methods that are not required by most programmers using the class, but that may be of interest to anyone creating subclass as part of a different package. Protected members are technically part of the API of a class – so they should be documented and changes may break other code relying on the API. Use the default package visibility for variables and methods that are internal implementation details, but may be used by cooperating classes in the same package. To do this, you need to use the package directive at the top of a class. Use private for variables and methods that are used only inside the class and should be hidden everywhere else.
4 Constructor chaining & default constructor 4.1 Inheritance Hierarchy More than two sub-classes can inherit from the same super class. A subclass can itself be a super class to other subclasses. The classes then form an inheritance hierarchy. It is important to be aware of how the Java compiler and interpreter deal with an inheritance hierarchy. We have already discussed how methods can be overridden and how member variables can be hidden. Inheritance also has implications for the constructors of an object. 4.2 Constructor Chaining The constructor of a class is called whenever an object of that class is instantiated. The constructor is also called when an instance of any subclass of that class is instantiated. For example, when an Employee object is instantiated, the constructor for the super class Parent is called. The constructor of the subclass is also called. The constructor of the super class is called first. It is possible to define a class without a constructor, but in this case the Java compiler implicitly adds an empty constructor to the class – so, strictly speaking, all classes have a constructor. The default constructor is one that takes no parameters. If the subclass constructor does not explicitly call the constructor of the super class, the Java compiler automatically inserts a call to the default constructor of the super class (the default constructor is the one that takes no parameters). Alternatively, the programmer can do this explicitly in the code by using the super() keyword i.e. 1
API: Application Programming Interface. This is a general programming term that refers to those parts of a program that are exposed for use by other programs and programmers. An API often also includes the documentation about the program. For Java, this means the JavaDoc documentation about the classes in the program. Page 5 of 12
8966474.doc
FBE – Computer Science Dept
super() //calls the constructor of the super class
If the super class has a constructor that takes parameters, it must be explicitly called by the programmer e.g. super(arg1, arg2) //calls the constructor of the super class
If the call to the super class constructor has the wrong number of type of parameters, the compiler will report an error. In the same way that an overriding method in a subclass often just adds functionality to the method in the super class, the constructor in a subclass may just add more steps to the super class constructor. Consider the Person and Employee classes again. The Person class might have a constructor like this: /** Constructor
*/
public Person (String firstName, String fathersName, String address, Date dateOfBirth) { name = firstName + " " + fathersName; this.address = address; this.dateOfBirth = dateOfBirth; }
Note the use of the this keyword – this accesses a variable whose scope is the object or instance. If the line 'this.address = address' were to read 'address = address' , it would only be setting the parameter variable address to be its own value, it would not be setting the address instance variable. The this keyword cannot be used in class methods (declared using the static keyword) – because class methods are not associated with instances, only with the class. The constructor for the Employee class would need to pass the name, address and date of birth values to the super class, and also set its own instance variables. So it might be like this: /** Constructor */ public Employee (String firstName, String fathersName, String
address, Date dateOfBirth, long salary, String dept, String rank) { //pass values to the constructor of the super class Person super(firstName, fathersName, address, dateOfBirth); this.salary = salary; department = dept; this.rank = rank; } Page 6 of 12
8966474.doc
FBE – Computer Science Dept
It is better to explicitly call the super class constructor, even it has no parameters (i.e. the call is just super()) as this makes it clear to another programmer that there is a super class involved. 4.3 Calling a different Constructor in the Same Class Sometimes, there may be different constructors for the same class. Constructors are differentiated by taking different parameter lists – a parameter list is different if one or more of the data types are different and/or if the number of parameters is different. The interpreter determines which constructor to call based on the number and types of the parameters being passed. For example, if the rank of an Employee is not known, it may not be passed in the constructor. In this case, rather than duplicating the code in the first constructor, we can just use this() to call the first constructor, and pass it a value of null for the rank. /** Constructor to use when rank is not available (default to null) */ public Employee (String firstName, String fathersName, String address, Date dateOfBirth, long salary, String dept) {
//call the main constructor, giving NULL as the rank this (firstName, fathersName, address, dateOfBirth, salary,
dept, null); }
5 Abstract Classes In Handout 1, the idea of an abstract class was mentioned. An abstract class is a class from which objects cannot be instantiated. To make a Java class abstract, the class must be declared using the abstract keyword. For example, if we want to ensure that a Person object is never instantiated, because a person must be an Employee (or some other, as yet undefined, subclass of Person), we can declare it like this: abstract class Person { //class body omitted }
The keyword abstract appears before the class keyword, but after the access modifier for the class (if any). Methods can also be declared as abstract. An abstract method does not have a body i.e. it has no statements. It only declares the method name, access modifier, return type and parameters. The method must have a semi-colon to indicate the empty body. All subclasses must define an implementation of the abstract method. Consider a program that defines classes for different shapes. All the shapes might have a common super class named Shape. It does not make sense to instantiate an Page 7 of 12
8966474.doc
FBE – Computer Science Dept
object of type Shape, as it must be a circle or a triangle or a square etc. So the Shape class would be abstract. If we want to ensure that all subclasses implement methods to calculate the area and the circumference of the shape, we can add abstract methods to the Shape class. public abstract class Shape { public abstract double area(); public abstract double circumference(); }
Any subclass of Shape must now provide its own implementation of the area and circumference methods. public class Circle extends Shape { protected double radius; /** * Constructor for objects of class Circle */ public Circle(double a_Radius) { }
radius = a_Radius;
public double calcArea() { return (Math.PI * radius * radius); } public double circumference() { return (2 * Math.PI * radius); }
}
Some other points about abstract classes: • • • • • •
If a class has one or more abstract methods, the class itself must be declared as abstract. Static, private and final methods cannot be abstract – because these types of method cannot be overridden by subclasses. A final class cannot have any abstract methods. If a subclass of an abstract class does not implement all the abstract methods it inherits, then that class is itself abstract. Objects of a subclass can only be instantiated if the subclass overrides all of the abstract methods of the super class and provides an implementation (a method body) for all of them. This is a concrete class. An abstract class can have non-abstract methods. These methods do not have to be overridden by subclasses, but they can be if necessary.
Page 8 of 12
8966474.doc
FBE – Computer Science Dept
5.1 Using Abstract Classes Code Example: Demos/Shapes (BlueJ project) To illustrate the convenience of abstract classes and methods, look at the Shapes example (code in Code Examples\Shapes). The class TestShapes creates an array of different shape objects. The array is defined as an array of Shape objects. Because Circle and Square are subclasses of Shape, they can be placed in the array. This holds for any variable – a variable can hold objects of the declared type of the variable (e.g. Shape ) or of any subtype of the declared type (e.g. Circle, Square). Here, the class is the type of the variable and subtype objects may be used wherever objects of a super type are expected. This is known as substitution (because the subtype can be substituted for the super type) and is similar to a widening conversion between primitive data types e.g. a short literal value can be assigned to an int variable. The code iterates through the shapes array and prints out the area of each element, by calling the area() method of each Shape object. The area() method for the Shape class does not have an implementation, but the interpreter determines which area method to invoke by checking the type of the object at the current element in the array. This is the OO concept of dynamic (late) binding. To see why the area() method must be declared in the super class as well as in the subclasses, take the Shape class and comment out the line that declares the area() method, compile the class and then try to compile TestShapes. The compiler will report an error like 'cannot resolve symbol' for 'method area()'. public class TestShapes { public static void main (String[] args) { //create an array of shapes Shape[] shapes = new Shape[3]; shapes[0] = new Circle (3.0); shapes[1] = new Square (3.0); shapes[2] = new Rectangle (2.0, 3.0); //get area of each shape for (int i=0; i<shapes.length ; i++) { System.out.println ("Area of shape at element " + i + "
is:" + shapes[i].area()); } } }
Casting Objects In the above example, the placing of a Circle object in an array of shapes was effectively a widening conversion.
Page 9 of 12
8966474.doc
FBE – Computer Science Dept
If we want to take an object from the array and work with it, we may want to assign it to a variable of type Circle. For example, add the following code into the TestShapes class, in the main method and try to compile: Circle c; c = shapes[0];
The compiler will report an 'incompatible types' error, stating 'found: Shape; required: Circle'. This is because an object of a super class of Circle, the type for c, cannot be assigned to c. A cast is necessary i.e. Circle c; c = (Circle) shapes[0];
This is similar to a narrowing conversion, because the object in the array is being converted from a Shape to a Circle i.e. from the super class to the subclass. 6 Interfaces Java allows a class to inherit from one other class only i.e. a class can have only one super class. However, there may be cases where it is necessary for a class to inherit from more than one source. The Java solution to this problem is interfaces. A class can implement one or more interfaces. An interface is a reference data type that is very similar to a class. For example, in the Shapes example used above, if a shape can be centred on a given point, the shape would need to inherit from the normal (non-centred) class for that shape. E.g., a CentredCircle class would inherit from Circle. But it would also need to inherit methods to set the centre point and to get the coordinates of the centre. Not all shapes are centred, so it is not ideal to put the centre point methods in the Shape super class. So we can instead put the methods in an interface. public interface Centred {
}
public void setCentre(double x, double y); public double getCentreX(); public double getCentreY();
Note that the interface can only have abstract methods – it cannot implement the methods. A subclass of the Circle class can now also implement this interface. A class that implements an interface must implement the methods in the interface. public class CentredCircle extends Circle implements Centred { //add instance fields for the centre point private double cx, cy; /** * Constructor for objects of class CentredCircle Page 10 of 12
8966474.doc
FBE – Computer Science Dept
*/ public CentredCircle(double cx, double cy, double a_Radius) {
}
super (a_Radius); this.cx = cx; this.cy = cy;
//implement the abstract methods in the Centred interface public void setCentre (double x, double y) { cx = x; cy = y;} public double getCentreX() { return cx;} public double getCentreY() { return cy;} }
Some points about interfaces: • An interface does not provide any implementations - because, as the name suggests, it supplies an interface, or API, for certain functionality. • An interface is similar to a class, but not the same as a class. It can contain methods and variables, like a class. But, an interface can define only abstract methods (i.e. no implementation) and final fields (constants). • An interface can declare only abstract methods, so the methods have no implementation. All interface methods are instance methods – they cannot be class methods because class methods cannot be abstract. • All methods in an interface are implicitly public - because an interface defines a public API • An interface cannot define instance variables (fields). A field is an implementation detail, while an interface is supposed to be an API, without any implementation. Hence, the only fields that can be defined in an interface are constants, and they must be declared as static and final. • Interfaces are often used to define large numbers of constants used by various classes in a program. This is similar to creating header files in C++ to contain a large number of constants. Any class that implements the interface inherits the constants in the interface; it is not necessary to prefix the constant names with the interface name. Like a class, an interface is a reference data type. Therefore, when a class implements an interface, instances of that class can be assigned to variables of the interface type. For example, see the class TestShapesInterface. This creates an array of shape objects and then iterates through the array to write out the area of each shape. It also tests each shape to see if it is also a Centred shape. If it is, the centre point is also written out. public class TestShapesInterface { public static void main (String[] args) { Page 11 of 12
8966474.doc
FBE – Computer Science Dept
//create an array of shapes Shape[] shapes = new Shape[3]; //add some shapes /*Exercise: * create new classes CentredSquare & CentredRectangle, and
replace shapes[1] and shapes[2] * below with instances of each of these. * */ shapes[0] = new CentredCircle (1.0,1.0,1.0); shapes[1] = new Circle (3.0); shapes[2] = new CentredCircle (2.3,4.5,3.4);
//get area of each shape for (int i=0; i<shapes.length ; i++) { System.out.println ("\nArea of shape at element " + i +
" is:" + shapes[i].area()); //if it is a Centred shape, also print out the centre
point coordinates //use instanceof operator to check if it is an instance of the Centred interface if (shapes[i] instanceof Centred) { Centred c = (Centred) shapes[i]; //have to cast to Centred - see what happens if you do not include the cast System.out.println ("Centre point is (" +
c.getCentreX() + "," + c.getCentreY() + ")"); } else { System.out.println ("Not a centred shape"); } }
} }
Notes prepared by: FBE Computer Science Department.
Page 12 of 12