Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can you pass an int array to a generic method in java?

Tags:

java

generics

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};
        // ???
    }
}
like image 894
blank Avatar asked Apr 25 '09 07:04

blank


3 Answers

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)

like image 188
Peter Lawrey Avatar answered Sep 18 '22 21:09

Peter Lawrey


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.

like image 20
newacct Avatar answered Sep 16 '22 21:09

newacct


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.

like image 29
waxwing Avatar answered Sep 16 '22 21:09

waxwing