CodeBunny
CodeBunny

Reputation: 2031

Class Cast Exception being thrown when I use a generic method

So, I am using 2D arrays for a platformer physics system I'm working on. Unfortunately, the 2D arrays require generic types for various reasons.

I'm dynamically building the surfaces with some java Collections (linkedlist to be precise) and then converting them to 2D arrays with some hacky trickery. This currently works, giving me the appropriate 2D generic array: *

LinkedList<PhysSurface<P>> leftSurfaces = new LinkedList<PhysSurface<P>>();
LinkedList<PhysSurface<P>> rightSurfaces = new LinkedList<PhysSurface<P>>();
LinkedList<PhysSurface<P>> topSurfaces = new LinkedList<PhysSurface<P>>();
LinkedList<PhysSurface<P>> bottomSurfaces = new LinkedList<PhysSurface<P>>();

// Add surfaces to the lists

GenericArray<PhysSurface<P>[]> base = new GenericArray<PhysSurface<P>[]>(4);
PhysSurface<P>[][] surfaces = base.elements();
surfaces[0] = leftSurfaces.toArray(new GenericArray<PhysSurface<P>>().elements());
surfaces[1] = rightSurfaces.toArray(new GenericArray<PhysSurface<P>>().elements());
surfaces[2] = topSurfaces.toArray(new GenericArray<PhysSurface<P>>().elements());
surfaces[3] = bottomSurfaces.toArray(new GenericArray<PhysSurface<P>>().elements());

However, when I try to put this all into a generic, static method like this:

@SuppressWarnings("unchecked")
public static <T> T[][] to2DArray(Collection<T>... collections)
{
    GenericArray<T[]> base = new GenericArray<T[]>(collections.length);
    T[][] array = base.elements();
    for(int i = 0; i < collections.length; i++)
        array[i] = collections[i].toArray(new GenericArray<T>().elements());
    return array;
}

And then I call the method like this:

PhysSurface<P>[][] surfaces = GenericsUtils.to2DArray(leftSurfaces, rightSurfaces, topSurfaces, bottomSurfaces);

Then everything crashes, giving me a ClassCastException, saying that it cannot convert type Object to type PhysSurface. The stack trace is here:

Exception in thread "main" java.lang.ClassCastException: [[Ljava.lang.Object; cannot be cast to [[Lcom.meg.utils._2D.platformer.phys.environment.surface.PhysSurface;
    at com.meg.chaos_temple.test.world.PhysTestWorld$TestMap.<init>(PhysTestWorld.java:487)
    at com.meg.chaos_temple.test.world.PhysTestWorld$TestPlayer.<init>(PhysTestWorld.java:245)
    at com.meg.chaos_temple.test.world.PhysTestWorld.<init>(PhysTestWorld.java:95)
    at com.meg.chaos_temple.main.ChaosDebug.createWorld(ChaosDebug.java:72)
    at com.meg.jrabbit.engine.main.BaseGame.start(BaseGame.java:56)
    at com.meg.jrabbit.engine.loop.Loop.run(Loop.java:44)
    at com.meg.jrabbit.engine.main.BaseGame.run(BaseGame.java:40)
    at com.meg.jrabbit.engine.main.StandardGame.run(StandardGame.java:85)
    at com.meg.chaos_temple.main.ChaosDebug.main(ChaosDebug.java:19)

As far as I can tell, the generic method is not using the generic type T, but instead defaulting to creating a Object[][] and attempting to cast up when it returns. If this is what's happening, why does this not work when put into a static method? And if not, what the heck is going on?


Upvotes: 2

Views: 715

Answers (1)

Joop Eggen
Joop Eggen

Reputation: 109547

Unfortunately T[][] array is the same as Object[][] array at run-time. Generic type parameters will be dropped at run-time; only the compiler knows about it.

The solution is to pass the object's class: Class<T>.

import java.reflect.Array;

...

public static <T> T[][] to2DArray(Class<T> klazz, Collection<T>... collections)
{
    T[][] array = (T[][]) Array.newInstance(klazz, collections.length,
                                                   collections[0].size());
    //...
        array[i] = (T[]) Array.newInstance(klazz, collections[i].size());
    return array;
}

The cast of Array.newInstance is necessary for the multidimensional result. No more @SuppressWarnings("unchecked")!

Instead of passing collections[0].size() it probably suffices to pass 0.

Upvotes: 3

Related Questions