Java Tutorial for Beginners

 

Subclasses: Implementing Inheritance

Inheritance is one of the basic principles of OOP. Inheritance is said to implement an is-a… relationship between objects. For example, in a bank, an investor is a customer, and so is a borrower. A class Customer could be used as a base class, or superclass, to deal with both types of customer.

Each of them will, however, have additional data and methods unique to the customer type. Subclasses could therefore be built from Customer to cater for investors and borrowers. Since they will both need to use the original data and methods from Customer, they will inherit these. Additional data and methods will be defined in the subclasses for the specific needs of each type. They are therefore said to extend the superclass.

Let’s look at how we code a class that is to inherit data and methods from a superclass. The class header for a subclass HouseLoan which is to extend the Loan class in the previous lesson would be coded:

class HouseLoan extends Loan {……}

Since additional data will be need to be stored, for example the serial number of the title deeds, these must be defined at the front of the subclass. The instance variables defined in the original class will be inherited, and need not be defined.

Constructors must be provided which will initialise these new instance variables. These constructors will usually call the constructors from the superclass once they have initialised their own variables; this is done by the statement

super(argument list). For example, the HouseLoan class may begin like this:

class HouseLoan extends Loan
{

private String TitleDeeds;
public HouseLoan(String Cust, double Amt, double Rt, String Deeds)
{
super(Cust, Amt, Rt);
TitleDeeds=Deeds;
}

public HouseLoan(String Cust, String Amt, String Rt, String Deeds)
{
super(Cust, Amt, Rt);
TitleDeeds=Deeds;
}

…..}

Methods will be inherited from the superclass, so that it is not necessary to redefine them in the subclass. However, in some circumstances subclasses may need these methods to work in a different way. In that case, they can be written in the subclass using the same name as the method in the superclass. The new method in the subclass will override or hide the method in the superclass.

Additional methods may be needed in the subclass. They can be coded as normal.

The methods defined in the subclass will not have access to the instance variables stored in the superclass. They can only be accessed by invoking the relevant methods of the superclass.

The full coding for the class HouseLoan, including extra methods for getting and setting the title deeds, could look like this:

class HouseLoan extends Loan
{

private String TitleDeeds;
public HouseLoan(String Cust, double Amt, double Rt, String Deeds)
{
super(Cust, Amt, Rt);
TitleDeeds=Deeds;
}

public HouseLoan(String Cust, String Amt, String Rt, String Deeds)
{

super(Cust, Amt, Rt);
TitleDeeds=Deeds;
}

public void setTitleDeeds (String deeds)
{

TitleDeeds=Deeds;
}

public String getTitleDeeds ()
{

return TitleDeeds;
}

}

Inheritance can occur over as many levels as required, for example class b can be written as a subclass of class a, and inherit from it. Class c can be written as a subclass of b, and inherit all data and methods from b, including those which it inherited from a. Class d could then be written as a subclass of c and so on.

Unlike some languages, Java does not allow multiple inheritance. In other words, a subclass can only extend one class, not many (although there may be several classes above it in the hierarchy). However, sometimes it may be necessary to ensure that a group of classes have certain methods in place. This can be achieved by using interfaces.

In the real world, there are many examples of interfaces which allow different components to fit together. For example, an electric drill made by one manufacturer may be able to make use of attachments supplied by another manufacturer, because the interface between the drill and the attachments is the same in both cases. Interfaces in Java therefore define what must be present for one component to succussfully work with another.

For example, the class util.Arrays contains a method named sort that can be used to sort a group of objects held in an array. However, this will only work if all the objects in the array implement an interface named Comparable. Classes that implement this interface must have a method named compareTo that compares itself with another object, and decides which order they should be placed in when sorted. The compareTo method must expect an object of class Object as an argument(in fact, all classes implicitly extend class Object in Java, so this applies to objects of all classes). To quote the Java documentation for the Comparable class, this method ‘Returns a negative integer, zero, or a positive integer as this object is less than, equal to, or greater than the specified object.’

A class can be made to implement an interface by adding the phrase ‘implements interface_name to the class header. The class must then define all the methods that are required by the interface.

A class Product that is to implement the Comparable interface may look like this:

import java.math.*;
public class Product implements Comparable
{

int productCode;
String description;
BigDecimal price;

public Product(int code, String desc, BigDecimal pr)
{

productCode = code;
description = desc;
price = pr;
}

public int getProductCode()
{

return productCode;
}

public int compareTo(Object p)
{

if(!p instanceof Product)) // See Note 1
return 1;
Product prod = (Product)p; // See Note 2
if(productCode < prod.getProductCode())
return –1;
else if(productCode == prod.getProductCode())
return 0;
else
return 1;
}
}

Note 1: The instanceof operator checks whether an object belongs to a specific class. Here, we check that the object supplied to this method actually is of class Product. If not, we return 1, so that all objects that are not products end up at the back of the list.

Note 2: If we simply coded this as
Product prod = p;
the compiler would mark it as an error, since the compiler sees p as being an Object, not a Product. By prefixing p with Product in brackets, we are telling the compiler to assume that it is a Product. This technique is known as ‘casting’.