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?
- (GenericArray is a custom class that uses variable-length arguments to get around the usual barriers to making generic arrays and then wraps the result. The actual constructor is GenericArray(T… elements).)
Unfortunately
T[][] arrayis the same asObject[][] arrayat 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>.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 pass0.