在Java中创建泛型类型的数组

时间:2022-02-20 19:22:29

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):

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

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!

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


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.

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

    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 ();
            }
        }
    }

5 个解决方案

#1


2  

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.

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

#2


1  

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

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

Prior to generics, array types X[] where X!=Object are important types in APIs. We couldn't use List which couldn't specify the component type. X[] was the most useful one. Now with generics, we better use List<X> instead of X[] in APIs. (JDK still use X[] in its APIs, even newer APIs, probably because they want to avoid dependencies among their packages)

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

That's for API. Within implementation details, we still need arrays, they are indispensible. However, Object[] is enough. Actually if Java keeps only Object[] and rid all other reference array types, nothing seriously bad would happen. All we need is arrays of objects to write our programs.

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

Of course, it would be silly to restraint ourselves from using X[] when it's available and easy to use.

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

It would be equally silly, when X is not a simple class, and X[] is hard to work with, to waste our time to make it work. Nothing good is really achieved. In such situations, just use Object[] and we'll save ourselves a lot of troubles.

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

So conclusion: externally, don't accept arrays, and don't give out arrays; internally, use X[] if there's no sweat, otherewise use Object[] and be happy.

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

#3


1  

Try guava's ObjectArrays.newArray(T[] reference, int length).

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

#4


0  

The problem with this Foo[][] is that it in fact is defined as a Outer<T>.Foo[][], but the VM can't control (with ArrayStoreExceptions) that you don't put Outer<S>.Foo objects in it - this is why you get the warning. (This is why generic arrays are generally not considered typesafe.)

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

If you make sure this is not the case, then you have no problem.

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

#5


0  

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

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

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

#1


2  

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.

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

#2


1  

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

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

Prior to generics, array types X[] where X!=Object are important types in APIs. We couldn't use List which couldn't specify the component type. X[] was the most useful one. Now with generics, we better use List<X> instead of X[] in APIs. (JDK still use X[] in its APIs, even newer APIs, probably because they want to avoid dependencies among their packages)

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

That's for API. Within implementation details, we still need arrays, they are indispensible. However, Object[] is enough. Actually if Java keeps only Object[] and rid all other reference array types, nothing seriously bad would happen. All we need is arrays of objects to write our programs.

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

Of course, it would be silly to restraint ourselves from using X[] when it's available and easy to use.

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

It would be equally silly, when X is not a simple class, and X[] is hard to work with, to waste our time to make it work. Nothing good is really achieved. In such situations, just use Object[] and we'll save ourselves a lot of troubles.

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

So conclusion: externally, don't accept arrays, and don't give out arrays; internally, use X[] if there's no sweat, otherewise use Object[] and be happy.

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

#3


1  

Try guava's ObjectArrays.newArray(T[] reference, int length).

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

#4


0  

The problem with this Foo[][] is that it in fact is defined as a Outer<T>.Foo[][], but the VM can't control (with ArrayStoreExceptions) that you don't put Outer<S>.Foo objects in it - this is why you get the warning. (This is why generic arrays are generally not considered typesafe.)

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

If you make sure this is not the case, then you have no problem.

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

#5


0  

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

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

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