3

I have a class Test and a method testMethod. I try to pass an int and an array of type T to the method, and it should return the int'th element of the array.

public class Test  {
    public static void main(String[] args)  {
        System.out.println(testMethod(1, new int[] {1,2,4}));
    }

    public static <T> T testMethod(int rank, T[] elements)  {
        return elements[rank];
    }
}

However I get a _method testMethodin class Test cannot be applied to given types_, T extends Object declared in method <T>testMethod(int,T[]).

I guess the issue is that the array is of basic type int. I tried changing it to Integer and it worked.

Is there a way to make it work, while still using basic numeric types like int or double or float?

Ans
  • 1,212
  • 1
  • 23
  • 51
  • This is because you are using `int[]`. Try it with `Integer[]`. `System.out.println(testMethod(1, new Integer[] {1,2,4}));`. – OldCurmudgeon Feb 07 '18 at 09:29
  • 1
    You could check [Why don't Java Generics support primitive types?](https://stackoverflow.com/questions/2721546/why-dont-java-generics-support-primitive-types), not really a duplicate but this is really the same idea – AxelH Feb 07 '18 at 10:03

4 Answers4

4

You have to overload the testMethod(int, T[]) method to pass an int[] array:

public static int testMethod(int rank, int[] elements) {
    return elements[rank];
}

Otherwise, you need to meet requirements of T[], T extends Object (which denies primitive types) and pass an Integer[] array:

public static <T> T testMethod(int rank, T[] elements)  {
    return elements[rank];
}

Now you can pass either int[] or Integer[]:

final Integer i1 = testMethod(1, new Integer[]{1, 2, 4});
final int i2 = testMethod(1, new int[]{1, 2, 4});

Is there a way to make it work, while still using basic numeric types like int or double or float?

Unfortunately, only by method overloading. Have a look at the Arrays.binarySearch methods (18 different signatures in there). Most of the signatures were designed only to cover all the types.

Would overloading require just copying the code from the main function though?

I am afraid, yes (with small type and naming changes). Find the difference :)

public static void parallelSort(long[] a, int fromIndex, int toIndex) {
    rangeCheck(a.length, fromIndex, toIndex);
    int n = toIndex - fromIndex, p, g;
    if (n <= MIN_ARRAY_SORT_GRAN ||
        (p = ForkJoinPool.getCommonPoolParallelism()) == 1)
        DualPivotQuicksort.sort(a, fromIndex, toIndex - 1, null, 0, 0);
    else
        new ArraysParallelSortHelpers.FJLong.Sorter
            (null, a, new long[n], fromIndex, n, 0,
             ((g = n / (p << 2)) <= MIN_ARRAY_SORT_GRAN) ?
             MIN_ARRAY_SORT_GRAN : g).invoke();
}

public static void parallelSort(float[] a, int fromIndex, int toIndex) {
    rangeCheck(a.length, fromIndex, toIndex);
    int n = toIndex - fromIndex, p, g;
    if (n <= MIN_ARRAY_SORT_GRAN ||
        (p = ForkJoinPool.getCommonPoolParallelism()) == 1)
        DualPivotQuicksort.sort(a, fromIndex, toIndex - 1, null, 0, 0);  // 1
    else
        new ArraysParallelSortHelpers.FJFloat.Sorter                     // 2
            (null, a, new float[n], fromIndex, n, 0,                     // 3
             ((g = n / (p << 2)) <= MIN_ARRAY_SORT_GRAN) ?
             MIN_ARRAY_SORT_GRAN : g).invoke();
}

Is it a good practice to copy paste with little changes such code into several functions?

It is definitely not. But do we have another way?

You could say that primitive boxing might be an option:

final int[] ints = {1, 2, 4};
final Integer i3 = testMethod(1, Arrays.stream(ints).boxed().toArray(Integer[]::new));

But does a good API force users to convert their data to fit the given signatures?

Andrew Tobilko
  • 48,120
  • 14
  • 91
  • 142
  • 1
    Hm, thanks, that's a good idea. I knew of overloading, but combination of overloading and generics might work even better, don't you think? After all I only need to overload for the several basic numeric types and the generic will make sure the rest is covered as well. – Ans Feb 07 '18 at 09:37
  • 2
    Would overloading require just copying the code from the main function though? Is it a good practice to copy paste with little changes such code into several functions? – Ans Feb 07 '18 at 09:45
  • Thank you for detailed answer! – Ans Feb 07 '18 at 10:19
1

You cannot use a primitive type in place of a generic type parameter.

You can pass an Integer array instead:

System.out.println(testMethod(1, new Integer[] {1,2,4}));
Eran
  • 387,369
  • 54
  • 702
  • 768
1

You can't use a primitive in a Generic type because this is not inheriting Object. See more about that in Why don't Java Generics support primitive types? . For the conversion into an Integer array in your case, you can use the Streamn API do the boxing :

public static Integer[] boxed(int[] array){
    return IntStream.of(array).boxed().toArray(Integer[]::new);
}

It is basicily the same as iterating the array and boxing the value... into another instance.

You need to do that each time you want to use a primitive array in a generic method... not perfect but this will allow you to convert every existing array without the need to changing the type of an existing variable (Integer can be null which add other problems).

int[] array = {1,2,4};
System.out.println(testMethod(1, boxed(array)));

EDIT:

Without having to change your calls, simply by overriding the method for the primitve int :

public static int testMethod(int rank, int[] array){
    return testMethod(rank, Arrays.stream(array).boxed().toArray(Integer[]::new));
}

public static long testMethod(int rank, long[] array){
    return testMethod(rank, Arrays.stream(array).boxed().toArray(Long[]::new));
}

public static double testMethod(int rank, double[] array){
    return testMethod(rank, Arrays.stream(array).boxed().toArray(Double[]::new));
}

NOTE: for some type, you will probably be forced to simply build the array with a loop ... this will be a bit more verbose.

AxelH
  • 14,325
  • 2
  • 25
  • 55
  • good point, +1, but now we're going to write `n` versions of the `boxed` method, aren't we? Instead of just consuming the API, we are building a lot of bridges first :) – Andrew Tobilko Feb 07 '18 at 10:22
  • 1
    I agree @AndrewTobilko, but reusing your example, `Arrays.binarySearch` as a lot of overloads ;) note that my example is not the design I would use for the overloads, this was just an example. Overloading `testMethod` for primitives would be my choice. No bridge needed in that case. – AxelH Feb 07 '18 at 10:25
  • Note @AndrewTobilko that the bridge _could_ be acceptable if we have more than one method to overload. Instead of overloading 10methods for each type, we provided the solution to convert the array. – AxelH Feb 07 '18 at 10:53
0

I think you already mentioned the solution to make this code work in your question.. the only we is that we have to use Integer instead of int..

public static void main(String[] args)  {
    System.out.println(testMethod(1, new Integer[] {1,2,4}));
}

public static <T> T testMethod(int rank, T[] elements)  {
    return elements[rank];
}

If you want to use int, you cannot use T[] and would have to use int[] instead..

Awanish
  • 327
  • 1
  • 3
  • 14