A. Introduction
Ian is a math student who is eager to find out the pattern of each term's last digit in various number sequences. For instance, in a geometric sequence 1, 2, 4, 8, 16, 32, 64, 128, 256, ..., the last digits are evenly distributed among 2, 4, 6, and 8.
But in the arithmetic sequence 1, 4, 7, 10, 13, 16, 19, 22, 25, ..., are 0-9 distributed evenly? What about other sequences? As a proficient Java coder, Ian quickly put together the class below:
public class LastDigitAnalyzer {
// int array to count the occurence of each digit (0-9)
private int[] counters = new int[10]; // counters[0], counters[1].... counter[9]
// The process method should logically takes a Sequence object and the number of terms as arguments
public void process(Sequence seq, int numOfTerms) {
for (int i = 1; i <= numOfTerms; i++) {
int value = seq.next(); // hope to get the next number in a sequence, for instance, 678
int lastDigit = value % 10; // get the last digit, for instance, 678%10 = 8
counters[lastDigit]++; // increase the counter for digit 8
}
}
// display the results
public void display(){
for(int i=0;i<counters.length;i++){
System.out.println(i+ ": " + counters[i]); }
}
}
Ian wishes to feed his process() method as many different types of sequences as possible. But does he have to write all the sequences by himself? As a matter of fact, his friends Henry and Anthony both have many classes that produce sequences. Henry has a public Random sequence, while Anthony has a public Square sequence. So how can Ian use these two classes?
B. The solution to the above scenario
It does not help much if Ian makes his LastDigitAnalyzer a subclass of either Henry's or Anthony's class because Java ONLY allows single inheritances. If he uses Henry's, he couldn't use anyone else's classes. Plus, LastDigitAnalyzer may need to have its own superclass, and the three students all prefer running their code independently.
So the problem boils down to that a class needs to use a service provided by multiple classes where each class provides a unique implementation. Ryan the CS student explains the concept of an interface.
An interface is a class-like construct that contains only constants and abstract methods. It cannot be instantiated by can be implemented by any number of other classes.
A class can implement as many interfaces as wish. When a class declares that it implements an interface, it will provide implementations to all abstract methods declared in the interface.
Interface names can be used to declare object types just as class names.
So Ian's problem can be solved by defining a Sequence interface as below:
public interface Sequence {
int next();
}
This interface contains a next() method which returns the next term in a sequence as int.
Now we can have Henry and Anthony implement this interface in their classes without interfering with existing functionality:
public class RandomSequence implements Sequence
{ . . .
public int next(){
return (int) (Integer.MAX_VALUE * Math.random()); }
}
public class SquareSequence implements Sequence
{ . . .
private int n;
public double foo(){ return 10*Math.random(); }
public int next() { n++; return n * n; }
}
Since the interface name can be used to declare object types, all Ian has to do is to use the interface name Sequence to specify the argument type of process() method. Both Henry's and Anthony's objects fit perfectly in this type. Here is the main method to test:
class Main {
public static void main(String[] args){
LastDigitAnalyzer dist1 = new LastDigitAnalyzer();
dist1.process(new SquareSequence(), 5000); // a SquareSequence object is a Sequence type
dist1.display();
LastDigitAnalyzer dist2 = new LastDigitAnalyzer();
Sequence rs = new RandomSequence(); // a RandomSequence object is a Sequence type
dist2.process(rs, 5000);
dist2.display();
}
}
C. Try this: Question 19-23 on page 160 of Barron's AP CS A