在Java中创建generics类型的数组

所以我知道你不能“轻松地”在Java中创建一个generics类型的数组(但你可以创建集合)。 我最近碰到了一个需要二维对象数组(即Generic)的情况。 这是一个“粗略”的概念,它看起来像什么(不完整,但我想尽可能简短):

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

所以代码中的某个地方我需要一个数组:

 foo = new Foo[width][height]; 

(我们知道不可能发生)。 但是,我试过这个:

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

虽然我不得不压制警告但编译器接受了。 我想我的问题是“这会不会让我陷入困境?成员”foo“永远不会暴露在外面(虽然类型Foo和Bar都是)。我知道它很难看,但它绝对有效,这使得我不必创建其他“psedu-kludge”,这可能会导致课程覆盖“外部”课程更令人头疼。提前感谢!


这可能会使事情更容易可视化

这更接近我实际做的事情; 当然,我已经意识到在Map类中有很多支持方法和其他逻辑,我为了简洁起见而遗漏了这些方法。

  import java.lang.reflect.Array; import java.util.ArrayList; import java.util.List; interface Cell { public void add (T t); public boolean remove (T t); public List getAll (); public Map getMap (); } class Map { protected BaseCell map[][]; public abstract class BaseCell implements Cell { private List contents; public BaseCell () { this.contents = new ArrayList (); } public void add (T t) { this.contents.add (t); } public boolean remove (T t) { return this.contents.remove (t); } public List getAll () { return this.contents; } public Map 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 map = new Map (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 (); } } } 

你正在做的是安全的,因为你正在控制未曝光的map 。 您可能应该将其设为私有而不受保护,否则扩展类可能会错误地操纵它。 您可以通过强制转换为运行时检查来消除编译器警告,如下所示:

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

然后,如果稍后某些代码可能以不兼容的方式更改,编译器警告将屏蔽掉,那么它至少会在构建map时因运行时exception而中断。 请记住,Generics只是在编译时被删除。

我认为在这种情况下最好的选择是简单地使用Object[]

在generics之前,数组类型X[]其中X!=Object是API中的重要类型。 我们无法使用无法指定组件类型的ListX[]是最有用的一个。 现在使用generics,我们最好在API中使用List而不是X[] 。 (JDK仍然在其API中使用X[] ,甚至更新的API,可能是因为他们希望避免其包之间的依赖关系)

那是API。 在实现细节中 ,我们仍然需要数组,它们是不可或缺的。 但是, Object[]就足够了。 实际上,如果Java只保留Object[]并删除所有其他引用数组类型,那么不会发生任何严重的错误。 我们所需要的只是编写程序的对象数组。

当然,当它可用且易于使用时,限制自己使用X[]是愚蠢的。

同样愚蠢的是,当X不是一个简单的类时, X[]很难处理,浪费我的时间来使它工作。 没有什么好事真的实现。 在这种情况下,只需使用Object[] ,我们就可以省去很多麻烦。

所以结论:外部,不接受数组,不给出数组; 在内部,如果没有汗水则使用X[] ,然后使用Object[]并且开心。

尝试guava的ObjectArrays.newArray(T []引用,int length) 。

这个Foo[][]的问题在于它实际上被定义为Outer.Foo[][] ,但是VM无法控制(使用ArrayStoreException s)你没有把Outer.Foo对象 – 这就是你得到警告的原因。 (这就是通用数组通常不被认为是类型安全的原因。)

如果您确定不是这种情况,那么您没有问题。

这将允许它工作(以及未经检查的强制转换,这对于这种情况是正常的,因为您希望拥有一个generics类型的变量)。 解决方案是实现只有Foo引用参数化类Outer.Foo ,因为你在Outer的范围内; 要获得实际的原始类,您需要将其明确限定为Outer.Foo

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