Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating Arrays of Generic Types in Java

So I know that you cannot "easily" create an array of a generic type in Java (but you can create collections). I recently ran across a situation where I needed a 2 dimensional array of objects (that were Generic). Here's a "rough" idea of what it looked like (not complete but I'm trying to be as brief 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 needed an array as such:

foo = new Foo[width][height];

(which we know can't happen). However, I tried this:

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

which the compiler accepted though I had to suppress warnings. I guess my question is "Is this going to nip me in the bud somewhere down the line? The member "foo" is never exposed to the outside (though the types Foo and Bar are). I know it's ugly but it definitely works and saved me from having to create some other "psedu-kludge" the would probably have caused classes overriding the "Outer" class more headaches. Thanks in advance!


This might make things easier to visualize

This is closer to what I'm actually doing; realizing of course that there are many support methods and other logic inside of the Map class that I've left out 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 ();
            }
        }
    }
like image 672
Dave Avatar asked Mar 25 '11 03:03

Dave


People also ask

Can you create an array of generic type?

No, we cannot create an array of generic type objects if you try to do so, a compile time error is generated.

How do you create an array of generic type of a class in Java?

Array#newInstance to initialize our generic array, which requires two parameters. The first parameter specifies the type of object inside the new array. The second parameter specifies how much space to create for the array.

Why we Cannot create a generic array in Java?

If generic array creation were legal, then compiler generated casts would correct the program at compile time but it can fail at runtime, which violates the core fundamental system of generic types.


1 Answers

What you're doing is safe because you're controlling the unexposed map. You should probably make it private though and not protected, otherwise extending classes could incorrectly manipulate it. You can get rid of the compiler warning by casting into a runtime check, like this:

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

Then if at some later point the code is potentially changed in an incompatible way that the compiler warning would mask out, it'll at least break early with a runtime exception at the construction of your map. Keep in mind of course that Generics are simply erased at compile time.

like image 138
WhiteFang34 Avatar answered Oct 02 '22 20:10

WhiteFang34