An object inherits various instance variables and methods from its superclass. However, the fundamental principles of inheritance also allow you to extend its superclass, as well as override inherited methods, replacing them with a different implementation. To illustrate this example, we will subclass our Fraction class with a MixedNumber class. Let’s get started!

Extending

How would we implement a mixed number? At initial glance, it looks pretty simple—and it is! A mixed number can be simply represented with an integer as the main number component, and a Fraction object for the fractional component (in fact, a class that uses another class as part of its data storage is known as a hybrid (sub)class). But the good part about inheritance is that the instance variables from its superclass—MixedNumber inherits numerator and denominator from Fraction, so we can simply use that. Therefore, the only new ivar we need to add is the integer for the numerical portion.

In Xcode, open up your FractionDemo project. Click on File > New File… In the left, click on Cocoa Class, and choose Objective-C Class, make sure it’s a subclass of NSObject, and click Next. Save the File Name as MixedNumber.m, and make sure that the check box below, ‘Also create “MixedNumber.h”‘ is checked. Click Finish. This should take you to the file listings in Xcode, and open up Fraction.h in the text editor. Import “Fraction.h”, and make it a subclass of Fraction. Rather than providing the full listing, try to figure out the code by yourself. If you’re stuck, you can download the full code at the end of the post. Add an NSInteger for the wholeNumber portion, and synthesize it.

In MixedNumber.m, add the following method implementations:

1. - (void)setWholeNumber:(NSInteger)number andNumerator:(NSInteger)num overDenominator:(NSInteger)denom {
2.     self.wholeNumber = number;
3.     self.numerator = num;
4.     self.denominator = denom;
5. }
6.
7. - (void)setWholeNumber:(NSInteger)number andFraction:(Fraction *)frac {
8.     self.wholeNumber = number;
9.     self.numerator = frac.numerator;
10.     self.denominator = frac.denominator;
11. }
12.
13. - (void)display {
14.     NSString *numberString = [[NSString alloc] initWithFormat:@"%d", self.wholeNumber];
15.     NSString *numeratorString = [[NSString alloc] initWithFormat:@"%d", self.numerator];
16.     NSString *denominatorString = [[NSString alloc] initWithFormat:@"%d", self.denominator];
17.     NSLog(@"%@+(%@/%@)", numberString, numeratorString, denominatorString);
18.     [denominatorString release];
19.     [numeratorString release];
20.     [numberString release];
21. }
22.
23. + (MixedNumber *)addMixedNumber:(MixedNumber *)num1 toMixedNumber:(MixedNumber *)num2 {
24.     // Store result in "result"
25.     MixedNumber *result = [[[MixedNumber alloc] init] autorelease];
26.     result.wholeNumber = num1.wholeNumber + num2.wholeNumber;
27.     result.numerator = num1.numerator * num2.denominator + num1.denominator * num2.numerator;
28.     result.denominator = num1.denominator * num2.denominator;
29.
30.     // Reduce
31.     if (result.numerator > result.denominator) {
32.         int extra = result.numerator / result.denominator;
33.         result.wholeNumber += extra;    // Taking advantage of integer division
34.         result.numerator -= extra * result.denominator;
35.
36.         int u = result.numerator;
37.         int v = result.denominator;
38.         int temp = 0;
39.
40.         // Euclid's procedure to find GCD (Greatest Common Denominator)
41.         // Don't worry about how this works, exactly.
42.
43.         while (v != 0) {
44.             temp = u % v;
45.             u = v;
46.             v = temp;
47.         }
48.
49.         result.numerator /= u;
50.         result.denominator /= u;
51.     }
52.
53.     return result;
54. }

Make sure to add the method declarations to the header.

A few things to note here:

1. The first two methods are similar to the set… methods of the Fraction class—nothing new or interesting here.
2. The -display method is interesting — it has the same name as its superclass, Fraction. This invokes the inheritance chain—the runtime will find the version of -display that is closest to MixedNumber. Therefore, it will call this latter implementation rather than Fraction’s.
3. The add method is a class method (for extra practice, declare and implement all four instance methods and the remaining three class methods). Here, we add the whole number portions, and properly add the fractional components. We reduce, first by checking and making sure that the fraction is actually less than one, and then using Euclid’s method to reduce our resulting fraction.

Note that a subclass can call one of its (direct) superclass’s overridden methods by using the super keyword:

[super display];

This works in much the same way as the self keyword, except that the latter refers to the current instance, while the former refers to the superclass.

A test routine for the MixedNumber class:

1. ...
2.     // bFraction is declared previously
3.     MixedNumber *aMixedNum = [[MixedNumber alloc] init];
4.     MixedNumber *bMixedNum = [[MixedNumber alloc] init];
5.     [aMixedNum setWholeNumber:3 andNumerator:2 overDenominator:4];
6.     [bMixedNum setWholeNumber:4 andFraction:bFraction];
7.
8.     NSLog(@"aMixedNum is"); [aMixedNum display];
9.     // Uses Fraction's reduce method on the fractional portion of MixedNumber
10.     NSLog(@"After reducing, aMixedNum is"); [aMixedNum reduce]; [aMixedNum display];
11.
13.     [aMixedNum display]; NSLog(@" + "); [bMixedNum display]; NSLog(@" = ");
14.     [[MixedNumber addMixedNumber:aMixedNum toMixedNumber:bMixedNum] display];
15.     // display is invoked on the return value of the add method
16.
17.     [bFraction release];
18.     [aMixedNum release];
19.     [bMixedNum release];
20.     ...

The code is rather self-explanatory, but make sure you understand which method is called, and which method is actually being found in the inheritance chain, especially when you start to override methods.

The output is:

1. aMixedNum is
2. 3+(2/4)
3. After reducing, aMixedNum is
4. 3+(1/2)