Robert
Robert

Reputation: 8609

subList for arrays - ClassCastException when casting to Generic Type

I've written a small utility method but it always produces a ClassCastException, any ideas why? and how to fix it?

<T> T[] subArray(int begin, int end, T[] array) {
    int size = end - begin;
    Object[] newArray = new Object[size];
    for (int i = 0; i < size; i++) {
        newArray[i] = array[begin + i];
    }
    return (T[]) newArray;
}

Here's the stack trace:

java.lang.ClassCastException: [Ljava.lang.Object;
at org.robert.distance.framework.FacadeTest.testSubArray(FacadeTest.java:48)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:592)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:76)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184)
at org.junit.runners.ParentRunner.run(ParentRunner.java:236)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:46)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)

Upvotes: 1

Views: 1780

Answers (4)

ILMTitan
ILMTitan

Reputation: 11017

I suspect the problem is you are returning an Object[]. Instead of using new Object[size], you need to create a new array of the correct type. One way to do that is to use Array.newInstance(Class<?> componentType, int length).

Something like:

<T> T[] subArray(int begin, int end, T[] array) {
    int size = end - begin;
    T[] newArray = (T[])Array.newInstance(array.getClass().getComponentType(), size);
    for (int i = 0; i < size; i++) {
        newArray[i] = array[begin + i];
    }
    return  newArray;
}

Upvotes: 5

BalusC
BalusC

Reputation: 1108642

Use Arrays#copyOfRange()

<T> T[] subArray(int begin, int end, T[] array) {
    return Arrays.copyOfRange(array, begin, end);
}

Heck, it even makes the whole subArray() method superfluous :) It only requires JDK 1.6. If you're not on it yet for some reason, here's an extract of relevance:

public static <T> T[] copyOfRange(T[] original, int from, int to) {
    int newLength = to - from;
    if (newLength < 0) throw new IllegalArgumentException(from + " > " + to);
    T[] copy = (T[]) Array.newInstance(original.getClass().getComponentType(), newLength);
    System.arraycopy(original, from, copy, 0, Math.min(original.length - from, newLength));
    return copy;
}

Upvotes: 7

Bozho
Bozho

Reputation: 597046

T[] isn't Object[] - try String[] ar = new Object[1];

You'd better use System.arraycopy(..). And the utility method Arrays.copyOf uses Array.newInstance(newType.getComponentType(), newLength); to create a new array, so combine these two methods to achieve your goal

Update: As noted in the comments, and in BalusC's answer, there is already Arrays.copyOfRange(..)

Upvotes: 4

Andrzej Doyle
Andrzej Doyle

Reputation: 103777

Yes, it's quite straightforward. You're creating an Object[] array, and then casting it to T[]. If T is not Object, this cast will fail.

Generics and arrays don't really mix very well. As ILMTitan says, you can use Array.newInstance, though then you'll have to have the caller pass an instance of Class<T> into your method. Although a better approach would be to use System.arrayCopy as this seems to be exactly what you're doing here.

Upvotes: 1

Related Questions