Objective-C Inheritance

Revision as of 20:22, 20 November 2009 by Neil (Talk | contribs)

Revision as of 20:22, 20 November 2009 by Neil (Talk | contribs)

PreviousTable of ContentsNext
Objective-C - Data Encapsulation, Synthesized Accessors and Dot NotationPointers and Indirection in Objective-C


In the chapter entitled An Overview of Objective-C Object Oriented Programming we covered the basic concepts of object-oriented programming and worked through an example of creating a new class using Objective-C. In that example, our new class was derived from the NSObject base class and as such inherited a number of traits from that parent class. In this chapter we will explore the subject of inheritance in greater detail.

Inheritance, Classes and Subclasses

The concept of inheritance brings something of a real-world view to programming. It allows a class to be defined that has a certain set of characteristics (such as methods and instance variables) and then other classes to be created which are derived from that class. The derived class inherits all of the features of the parent class and typically then adds some features of its own.

By deriving classes we create what is often referred to as a class hierarchy. The class at the top of the hierarchy is known as the base class or root class and the derived classes as subclasses or child classes. Any number of subclasses may be derived from a class. The class from which a subclass is derived is called the parent class.

Classes need not only be derived from a root class. For example, a subclass can also inherit from another subclass with the potential to create large and complex class hierarchies.

In Objective-C a subclass can only be derived from a single direct parent class. This is a concept referred to as single inheritance.

An Objective-C Inheritance Example

As with most programming concepts the subject of inheritance in Objective-C is perhaps best illustrated with an example. In the An Overview of Objective-C Object Oriented Programming we created a class called BankAccount designed to hold a bank account number and corresponding current balance. The class contained both instance variables and instance methods. The interface and implementation declarations for this class are reproduced below:

@interface BankAccount: NSObject
{
        double accountBalance;
        long accountNumber;
}
-(void) setAccount: (long) y andBalance: (double) x;
-(void) setAccountBalance: (double) x;
-(double) getAccountBalance;
-(void) setAccountNumber: (long) y;
-(long) getAccountNumber;
-(void) displayAccountInfo;
@end

@implementation BankAccount

-(void) setAccount: (long) y andBalance: (double) x;
{
        accountBalance = x;
        accountNumber = y;
}
-(void) setAccountBalance: (double) x
{
        accountBalance = x;
}

-(double) getAccountBalance
{
        return accountBalance;
}

-(void) setAccountNumber: (long) y
{
        accountNumber = y;
}

-(long) getAccountNumber
{
        return accountNumber;
}

-(void) displayAccountInfo
{
        NSLog (@"Account Number %i has a balance of %f", accountNumber, accountBalance);
}
@end

As we can see from the declaration, this class is a subclass of the NSObject base class and contains a number of instance variables and instance methods. Though this is a somewhat basic class it does everything necessary if all you need it to do is store an account number and account balance. Suppose, however, that in addition to the BankAccount class you also needed a class to be used for savings accounts. A savings account will still need to hold an account number and a current balance and methods will still be needed to access that data. One option would be to create an entirely new class, one that duplicates all of the functionality of the BankAccount class together with the new features required by a savings account. A more efficient approach would be to create a new class that is a subclass of the BankAccount class. The class will then inherit all the features of the BankAccount class but can then be extended to add the additional functionality required by a savings account.

To create a subclass of BankAccount that we will call SavingsAccount we simply write the appropriate interface and implementation structure, this time specifying BankAccount instead of NSObject as the parent class (inheritance passes down through all levels of the class hierarchy so SavingsAccount will still inherit from NSObject via the BankAccount parent class):

@interface SavingsAccount: BankAccount
{
}
@end

@implementation SavingsAccount

@end

Note that although we have yet to add any instance variables or methods, the class has actually inherited all the methods and variables of the parent BankAccount class. We could, therefore, create an instance of the SavingAccount class and set variables and call methods in exactly the same way did with the BankAccount class in An Overview of Objective-C Object Oriented Programming. That said we haven't really achieved anything unless we actually take steps to extend the class.


Extending the Functionality of a Subclass

So far we have been able to create a subclass that contains all the functionality of the parent class (including the functionality the parent class inherited from its parent class and so on). In order for this exercise to make sense, however, we now need to extend the subclass so that it has the features we need to make it useful for storing savings account information. To do this, we simply add the instance variables and methods that provide the new functionality, just as we would for any other class we might wish to create:

@interface SavingsAccount: BankAccount
{
        float interestRate;
}
-(float) calculateInterest;
-(void) setInterestRate: (float) y;
@end

@implementation SavingsAccount
-(float) calculateInterest
{
        return interestRate * accountBalance;
}

-(void) setInterestRate: (float) y
{
        interestRate = y;
}
@end

With these changes made we now have a class that inherits the features of the BankAccount class but now has an additional instance variable in the form of interestRate and two new methods named calculateInterest and setInterestRate. The rest of the functionality we needed we essentially got for free by inheriting from the BankAccount class.

Overriding Inherited Methods

When using inheritance it is not unusual to find a method in the parent class that almost does what you need, but requires modification to provide the precise functionality you need. That said, it is also possible you'll inherit a method with a name that describes exactly what you want to do, but it actually does not come close to doing what you need. One option in this scenario would be to ignore the inherited method and write a new method with an entirely new name. A better option is to override the inherited method and write a new version of it in the subclass.

Before proceeding with an example, there are two rules that must be obeyed when overriding a method. Firstly, the overriding method in the subclass must take exactly the same number and type of arguments as the overridden class in the parent. Secondly, the new method must have the same return type and the parent method.

In our BankAccount class we have a method called displayAccountInfo that displays the bank account number and current balance held by an instance of the class. In our SavingsAccount subclass we might also want to output the current interest rate assigned to the account. To achieve this, we simply declare a new version of the displayAccountInfo method:

@interface SavingsAccount: BankAccount
{
        float interestRate;
}
-(float) calculateInterest;
-(void) setInterestRate: (float) y;
-(void) displayAccountInfo;
@end

@implementation SavingsAccount
-(float) calculateInterest
{
        return interestRate * accountBalance;
}

-(void) setInterestRate: (float) y
{
        interestRate = y;
}

-(void) displayAccountInfo
{
        NSLog (@"Account Number %i has a balance of %f and is earning %f interest", accountNumber, accountBalance, interestRate);
}
@end

Now that we have completed work on our SavingsAccount class we can write some code to create an instance and start manipulating the object using our inherited, overridden and subclass specific methods:

int main (int argc, const char * argv[])
{
        NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

        SavingsAccount *account1 = [[SavingsAccount alloc] init];

        [account1 setAccount: 4543455 andBalance: 3010.10];

        [account1 setInterestRate: 0.001];

        float interestEarned = [account1 calculateInterest];

        NSLog (@"Interest earned = %f", interestEarned);

        [account1 displayAccountInfo];

        [account1 release];

        [pool drain];

        return 0;
}

PreviousTable of ContentsNext
Objective-C - Data Encapsulation, Synthesized Accessors and Dot NotationPointers and Indirection in Objective-C