I'm playing around with some code katas and trying to get a better understanding of java generics at the same time. I've got this little method that prints arrays like I like to see them and I have a couple of helper methods which accept an array of 'things' and an index and returns the array of the 'things' above or below the index (it's a binary search algorithm).
Two questions,
#1 Can i avoid the cast to T in splitBottom and splitTop? It doesn't feel right, or I'm going about this the wrong way (don't tell me to use python or something .. ;) )
#2 Do I have to write seperate methods to deal with primitive arrays or is there a better solution?
public class Util {
public static <T> void print(T[] array) {
System.out.print("{");
for (int i = 0; i < array.length; i++) {
System.out.print(array[i]);
if (i < array.length - 1) {
System.out.print(", ");
}
}
System.out.println("}");
}
public static <T> T[] splitTop(T[] array, int index) {
Object[] result = new Object[array.length - index - 1];
System.arraycopy(array, index + 1, result, 0, result.length);
return (T[]) result;
}
public static <T> T[] splitBottom(T[] array, int index) {
Object[] result = new Object[index];
System.arraycopy(array, 0, result, 0, index);
return (T[]) result;
}
public static void main(String[] args) {
Integer[] integerArray = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
print(integerArray);
print(splitBottom(integerArray, 3));
print(splitTop(integerArray, 3));
String[] stringArray = {"one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten"};
print(stringArray);
print(splitBottom(stringArray, 3));
print(splitTop(stringArray, 3));
int[] intArray = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
// ???
}
}
Generics don't handle primitives in a consistent fashion. This is because Generics are not like templates in C++, it is just a compile time addition to a single class.
When generic are compiled, you end up with Object[] in the above example as the implementing type. As int[] and byte[] etc, do not extend Object[] you cannot use them inter-changeably even if the code involved would be identical (again generics are not templates)
The only class int[] and Object[] share is Object. You can write the above methods Object as the type (see System.arraycopy, Array.getLength, Array.get, Array.set)
1 Can i avoid the cast to T in splitBottom and splitTop? It doesn't feel right, or I'm going about this the wrong way (don't tell me to use python or something .. ;) )
Not only can you not avoid it, but you shouldn't do it. In Java, different types of arrays are actually different runtime types. An array that was created as an Object[]
cannot be assigned to a variable of AnythingElse[]. The cast there will not fail immediately, because in generics the type T is erased, but later it will throw a ClassCastException when code tries it to use it as a Something[] as you promised them, but it is not.
The solution is to either use the Arrays.copyOf...
methods in Java 6 and later, or if you are using an earlier version of Java, use Reflection to create the correct type of array. For example,
T[] result = (T[])Array.newInstance(array.getClass().getComponentType(), size);
2 Do I have to write seperate methods to deal with primitive arrays or is there a better solution?
It is probably best to write separate methods. In Java, arrays of primitive types are completely separate from arrays of reference types; and there is no nice way to work with both of them.
It is possible to use Reflection to deal with both at the same time. Reflection has Array.get()
and Array.set()
methods that will work on primitive arrays and reference arrays alike. However, you lose type safety by doing this as the only supertype of both primitive arrays and reference arrays is Object
.
Question 1: Casting arrays doesn't work like you expect. A String is an Object, but a String array isn't an Object array.
Try to use something like:
public static <T> T[] splitTop(T[] array, int index) {
T[] result = Arrays.copyOfRange(array, index + 1, array.length);
return result;
}
Question 2: For arrays of primitives my function obviously doesn't work either. There is no elegant solution to it - look at for example the Arrays library which have several copies of essentially the same method for each primitive array type.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With