tags:

views:

63

answers:

4

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)
+4  A: 

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(..)

Bozho
Another option is using `Arrays.copyOfRange(...)`, possibly performing slightly worse than `System.arraycopy(...)`
MarcoS
@MarcoS: `Arrays.copyOfRange(...)` is only available in Java 1.6+
newacct
@newacct: you're right, but the question did not state any restriction on Java version
MarcoS
+5  A: 

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;
}
ILMTitan
How come the array does know what is its component type?
OscarRyz
@Sup: the exact `Class` is always known during runtime.
BalusC
+1  A: 

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.

Andrzej Doyle
+5  A: 

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;
}
BalusC
how I tend to forget these useful methods :) (+1)
Bozho