Polymorphism: Putting It All Together
Steven J. Zeil
1 Combining Techniques within a Class
As we have seen, no one technique for polymorphism covers all the bases. In particular, for primitive types we are generally limited to overloading. Class types give us the option of working with inheritance or generics. That means that to provide a function that work with all data types, we often combine techniques. For example, if we were committed to providing insertInOrder
for all types, we would wind up with something like:
import java.util.ArrayList;
public class Utilities {
public static int insertInOrder(int value, int[] intoArray, int size) {
int i = size;
while(i > 0 && value < intoArray[i-1]) {
intoArray[i] = intoArray[i-1];
--i;
}
intoArray[i] = value;
return i;
}
public static int insertInOrder(double value, double[] intoArray, int size) {
int i = size;
while(i > 0 && value < intoArray[i-1]) {
intoArray[i] = intoArray[i-1];
--i;
}
intoArray[i] = value;
return i;
}
public static int insertInOrder(float value, float[] intoArray, int size) {
int i = size;
while(i > 0 && value < intoArray[i-1]) {
intoArray[i] = intoArray[i-1];
--i;
}
intoArray[i] = value;
return i;
}
public static int insertInOrder(char value, char[] intoArray, int size) {
int i = size;
while(i > 0 && value < intoArray[i-1]) {
intoArray[i] = intoArray[i-1];
--i;
}
intoArray[i] = value;
return i;
}
public static int insertInOrder(byte value, byte[] intoArray, int size) {
int i = size;
while(i > 0 && value < intoArray[i-1]) {
intoArray[i] = intoArray[i-1];
--i;
}
intoArray[i] = value;
return i;
}
public static <T extends Comparable<T>> int insertInOrder(T value, T[] intoArray, int size) {
int i = size;
while(i > 0 && value.compareTo(intoArray[i-1]) < 0) {
intoArray[i] = intoArray[i-1];
--i;
}
intoArray[i] = value;
return i;
}
}
2 java.utils.Arrays
If you think that this is overkill, try looking at the java.util.Arrays
class.
- At the top of the list of provided functions, you will see
binarySearch
, an overloaded version of the function introduced in chapter 7 of your text.- This comes in two forms, one that searches the entire array, and one that searches a portion of the array between two indices.
- But notice that each of those forms has overloaded versins for each of the primitive types, plus an inheritance-based form for all class types.
- Look also at the descriptions of
compare
,copyOf
,equals
,mismatch
, andsort
, each of which should probably be in your personal “toolkit” of functions for working with arrays.Why write, for example,
int[] newArray = new int[oldArray.length]; for (int i = 0; i < oldArray.length) { newArray[i] = oldArray[i]; }
when you could just write:
int[] newArray = Arrays.copyOf(oldArray, oldArray.length);
It may not save more than a few lines of code, but even that saves you from multiple opportunities for making coding mistakes. Perhaps more importantly, these funcitonc can make your code easier to read. You don’t have to figure out “Oh, I see. This loop is copying all the elements from one array to another.” Instead the function name
copyOf
tells you exactly what is going on.- And if you are inclined to discount the importance of ease of reading by saying, “Oh, nobody else is going to look at this code.”, keep in mind that you will be reading your code. Have ever had the experience of returning to some code that you wrote a day or two earlier and saying to yourself, “What in the world was I trying to do here? Why did I write that?” If you have never had this happen to you, you ae fortunate indeed (or just very new to programming).
3 System.arrayCopy
Another useful utility is in java.lang.System
, (the same package from which we get System.in
and System.out
):
public class System {
⋮
/**
* Copies an array from the specified source array, beginning at the specified position, to the specified
* position of the destination array.
*
* @param src the source array.
* @param srcPos starting position in the source array.
* @param dest the destination array.
* @param destPos starting position in the destination data.
* @param length the number of array elements to be copied.
*
* @throws IndexOutOfBoundsException if copying would cause access of data outside array bounds.
* @throws ArrayStoreException if an element in the src array could not be stored into the
* dest array because of a type mismatch.
* @throws NullPointerException if either src or dest is null.
*/
static void <E,T> arraycopy(E[] src, int srcPos, T[] dest, int destPos, int length) {
⋮
}
This is only available for class objects, not primitives.
For example, given this code
String[] input = {"abc", "def", "ghi", "jkl"};
String[] output = {"000", "001", "010", "011", "100"};
System.arraycopy(input, 1, output, 2, 2);
the array output
would contain {"000", "001", "def", "ghi", "100"}
.
As with many of the java.utils
functions we have just discussed, this can allow you to remove a common loop pattern from your code, potentially making just that little bit easier to read.
The implementation of this is interesting, because it highlights a common mistake programmers can make when doing their own copies.
Here is a pretty straightforward approach to implementing this function:
public static <E, T> void arraycopy(E[] src, int srcPos,
T[] dest, int destPos, int length) {
for (int k = 0; k < length; ++k) {
Object toCopy = src[srcPos + k];
dest[destPos + k] = (T) toCopy;
}
}
And that would work for the sample test above. But suppose that we wanted to copy a portion of one array into a different portion of itself.
-
This works:
String[] array = {"a", "b", "c", "d", "e", "f"}; System.arraycopy(array, 3, array, 2, 2);
After running that , the array would contain
{"a", "b", "d", "e", "e", "f"}
. The function copied elements [3..4] (“d” and “e”) into positions [2..3], overwriting the original contents (“c” and “d”).\clearRight
-
This does not work:
String[] array = {"a", "b", "c", "d", "e", "f"}; System.arraycopy(array, 1, array, 2, 2);
We would expect the final array to contain,
{"a", "b", "b", "c", "e", "f"}
. Instead, we will find it contains{"a", "b", "b", "b", "e", "f"}
. What happened to the “c”? Why didn’t it get copied?

To see why this happens, consider the copies step by step. First we copy the “b” from position 1 into position 2. Then we copy from position 2 to position 3. Position 2 used to hold a “c” but has already been overwritten with a “b”, so we wind up copying the “b” again.
To fix this, we test to detect when this kind of overlapping ranges can occur, and, if so, copy “backwards” from the higher-numbered positions towards the lower ones.
public static <E, T> void arraycopy(E[] src, int srcPos,
T[] dest, int destPos, int length) {
if ((Object) src != (Object) dest || srcPos >= destPos) { ➀
for (int k = 0; k < length; ++k) {
Object toCopy = src[srcPos + k];
dest[destPos + k] = (T) toCopy;
}
} else {
for (int k = length - 1; k >= 0; --k) { ➁
Object toCopy = src[srcPos + k];
dest[destPos + k] = (T) toCopy;
}
}
}

- ➀ Here we test to see if the
source
anddest
arrays are at the same addresses and if thesrcPos
comes before thedestPos
. - ➁ If both of those are true, we come to ➁, where we have a loop that runs “bakcawards” from the higher numbers towards the lower ones.
As it happens, copying data from one part of an array to another is pretty common, and it’s easy to make the potential problem when the range of positions being copied from and being copied into overlap. So it’s nice to have a utility function like this that is smart enough to cope.
Because we will make frequent use of this function, it’s important to know to know its complexity.
You can run this function as an animation if you want to see it in action.
Look at the code for arraycopy
. Can you say what its worst case complexity will be?