Creating arrays of generic types in Java

So, I know that you cannot "easily" create an array of a general type in Java (but you can create collections). Recently, I came across a situation where I needed a 2-dimensional array of objects (which were Generic). Here's a “crude” idea of ​​how it looked (not complete, but I try to be as concise as possible):

class Outer<T> { private Foo[][] foo; abstract class Foo extends Blah<T> { public List<T> getContents (); } abstract class Bar extends Foo { ... } } 

So, somewhere in the code, I need an array as such:

 foo = new Foo[width][height]; 

(which, as we know, cannot happen). However, I tried this:

 foo = (Foo[][])Array.newInstance (Foo.class, new int[]{getWidth (), getHeight ()}); 

which was accepted by the compiler, although I had to suppress warnings. I suppose my question is: “Am I going to rob me of my kidney somewhere along the line? The foo member is never exposed (though Foo and Bar types). I know it's ugly, but it definitely works and saved from the need to create some other "psedu-kludge" I would probably be called by classes that would redefine the "outer" class for more headaches. Thanks in advance!


This can facilitate visualization.

This is closer to what I'm actually doing; Of course, there are many support methods and other logic inside the map class that I forgot for brevity.

  import java.lang.reflect.Array; import java.util.ArrayList; import java.util.List; interface Cell<T> { public void add (T t); public boolean remove (T t); public List<T> getAll (); public Map<T> getMap (); } class Map<T> { protected BaseCell map[][]; public abstract class BaseCell implements Cell<T> { private List<T> contents; public BaseCell () { this.contents = new ArrayList<T> (); } public void add (T t) { this.contents.add (t); } public boolean remove (T t) { return this.contents.remove (t); } public List<T> getAll () { return this.contents; } public Map<T> getMap () { return Map.this; } abstract public boolean test (); } public class SpecialCell extends BaseCell { @Override public boolean test() { return true; } } public class SpecialCell2 extends BaseCell { @Override public boolean test() { return false; } } @SuppressWarnings("unchecked") public Map (int width, int height) { this.map = (BaseCell[][])Array.newInstance(BaseCell.class, new int[] {width, height}); for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { if (Math.random() < .5) { this.map[x][y] = new SpecialCell (); } else { this.map[x][y] = new SpecialCell2 (); } } } } public BaseCell getCellAt (int x, int y) { return this.map[x][y]; } } public class Junk { /** * @param args */ public static void main(String[] args) { class Occupant { } Map<Occupant> map = new Map<Occupant> (50, 50); map.getCellAt(10, 10).add(new Occupant ()); map.getCellAt(10, 10).getMap (); for (int y = 0; y < 50; y++) { for (int x = 0; x < 50; x++) { System.out.print (map.getCellAt (x, y).test () ? "1" : "0"); } System.out.println (); } } } 
+6
java arrays generics
source share
5 answers

What you do is safe because you control an unexposed map . You should probably make it private, though not secure, otherwise class extensions might mis-manipulate it. You can get rid of the compiler warning by performing a validation check, for example:

 this.map = BaseCell[][].class.cast(Array.newInstance(BaseCell.class, new int[] { width, height })); 

Then, if at some later point in time the code is potentially modified in an incompatible way that the compiler warning is masked, it will at least break early with an exception at runtime when building your map . Keep in mind, of course, that Generics are simply erased at compile time.

+2
source share

I think the best choice in this situation is to simply use Object[] .

Prior to generics, the array types are X[] , where X!=Object are important types in the API. We could not use List , which could not indicate the type of component. X[] was most useful. Now with generics, we better use List<X> instead of X[] in the API. (The JDK still uses X[] in its APIs, even newer APIs, probably because they want to avoid dependencies between their packages)

This is for the API. In implementation details, we still need arrays, they are irreplaceable. However, Object[] enough. In fact, if Java saves only Object[] and eliminates all other types of reference arrays, nothing serious bad will happen. All we need are arrays of objects for writing our programs.

Of course, it would be foolish to restrain the use of X[] when it is available and easy to use.

It would be just as stupid when X not a simple class, and X[] hard to work to waste its time to make it work. Nothing good has been achieved. In such situations, just use Object[] , and we will save a lot of trouble.

So, the conclusion: from the outside, do not accept arrays and do not give out arrays; internally use X[] if there is no sweat, otherwise use Object[] and be happy.

+1
source share
+1
source share

The problem with this Foo[][] is that it is actually defined as Outer<T>.Foo[][] , but the VM cannot control (with ArrayStoreException s) that you do not put Outer<S>.Foo objects in it Outer<S>.Foo this is why you get a warning. (This is why shared arrays are usually not considered typical.)

If you are convinced that this is not so, then you have no problem.

0
source share

This will allow it to work (along with an uncontrolled act, which is normal for this situation, since you want to have a variable that is an array of a generic type). The solution understands that only Foo belongs to the parameterized class Outer<T>.Foo , since you are inside the scope of Outer<T> ; and to get the actual raw class, you need to explicitly specify it as Outer.Foo :

 foo = (Foo[][])new Outer.Foo[width][height]; 
0
source share

All Articles