Laboratory: Generics and Interfaces
CSC 207 Algorithms and Object Oriented Design Spring 2009

Summary: In this lab, you will explore both interfaces and Java generics by writing a generic interface and creating classes that implement it.

Preparation

  1. Create a directory for this lab:
      mkdir somewhere/generics
  2. Go to your new directory:
      cd somewhere/generics

Exercises

In this lab, you'll be creating an interface that allows one to extract individual components from some linearly indexed object. This is exactly analagous to the behavior of arrays. However, not all objects are naturally stored as arrays. While we may want to provide that kind of access, it may not be wise to store them linearly.

Thus, you will create an interface that indicates this kind of access and create some implementations that provide it on top of some type that are not necessarily stored in a linearly indexed fashion.

Exercise 1

  1. Create an interface called Indexable that is parameterized by a single type, let's call it ComponentType.
  2. Add two methods two your interface:
    • length, which returns an int indicating the number of valid indices that may be accessed.
    • get, which takes an int for an index (zero-based) and returns the object of ComponentType at that index. The function should throw an IndexOutOfBoundsException if the index is greater than length()

Exercise 2

First we will declare a simple type of indexable object--something to index individual characters in a string.
  1. Create a class called IndexableString that implements the Indexable interface. What should the parameterized type be (what are the components that will be returned by get)?
  2. Add stubs for the methods required by the interface, but do not implement them yet (i.e., you can return 0 or null)
  3. Check to see that your new class compiles. If you need help, be sure to talk with a neighbor or the instructor.
  4. Why might it be convenient if we could say "IndexableString IS-A String"?
  5. Create your class so that this is true (i.e., create a subclass relationship using extends).
  6. Now re-compile your code. What happens? Why?
  7. It turns out that in this case, Java prevents us from creating an IS-A relationship, so the best we can do is to use a HAS-A relationship. Add a member and constructor so that IndexableString does indeed have a String object to work with in its two required functions.
  8. Now, go ahead and implement the two functions required by the interface. If you explore the String object methods, it shouldn't be very hard!

Exercise 3

As it turns out strings are fairly naturally linearly indexed, since that is essentially how they are stored in memory (though Java shields us from this, unlike C). The individual (base 10) digits of integers are not naturally stored this way, so it might be useful to provide an interface for this in some applications.

Let's say that our convention will be that indexing starts with the right-most digit (the "ones place"), and is zero based. Thus, get(2) on an IndexableInteger should return the digit in the "hundreds" place, or the third digit from the right.

How should we handle the indexing of negative numbers? We'll let's say that the length only includes the number of digits, and that indexing always returns positive numbers.

  1. Create a class called IndexableInteger that implements the Indexable interface. What should the parameterized type be (what are the components that will be returned by get)?
  2. Once again, we would really like the IS-A relation to be true, but Integer suffers from the same issue as String. Use similar techniques for a HAS-A relationship. Note that you may want to use the primitive type, rather than the class wrapper to simplify your two method implementations.
  3. Create stub methods for the interface as before.
  4. How can we implement length? Algorithmically, speaking, this is the number of times we can divide the number by 10 before we get a number that is less than 10.
  5. Write a small test program to see if your length function works correctly. Be sure to test on negative numbers and numbers that are between -10 and 10.
  6. The implementation of get should be similar, but there are a few special cases to watch out for. The essence of the algorithm is this. If the magnitude of the number is less than 10 and the index is greater than 0, that's a problem. If the index is 0 then you can just return the number mod 10. If the index is greater than zero, then we need to "shift off" the first "index minus one" digits by dividing by 10 "index" times. If at any point before we are done we get a result that is less than 10, we have a problem. Otherwise, when are are done we can take the shifted result mod 10 for our answer.
  7. Test your implementation on several numbers.

Exercise 4

Now that you've had some practice with declaring interfaces and implementing some classes that make use of generics (even though these implementations are not generic at all!), now you can do some very generic things. (But don't tell your parents that you came to college to do generic things in class.)
  1. Create a main class called TestIndexable.
  2. In the main method of your class, create an ArrayList of Indexable objects.
  3. Add several of your indexable strings and integers to the ArrayList.
  4. Use Java's "enhanced" for loop to print the component at the second index of every item in the list.
Created: Jerod Weinman, 15 January 2009