如何创建通用数组?

5

可能是重复问题:
Java如何创建泛型数组

如何在Java中创建T[]类型的数组?我不能使用Arrays.newInstance(),因为我没有Class<T>对象。是否有一个泛型版本的newInstance方法呢?

我的方法原型如下:

public <T> T[][] distribute(T ... balls) {
   T[][] answer = ????

   // filling answer with data

   return answer;

}

更新

抱歉,在上面的例子中,我可以从balls中获取类。但是假设我没有这样的变量。

public <T> T[][] distribute() {
   T[][] answer = ????

   // filling answer with data

   return answer;

}

或者
class<T> {
   public T[][] distribute() {

      T[][] answer = ????

      // filling answer with data

      return answer;

   }
}

更新2

这个例子也不起作用:

public abstract class GenericArray<T> {

abstract public T create();

public T[] gen1() {
    T[] ans = (T[]) new Object[3];
    ans[0] = create();
    ans[1] = create();
    ans[2] = create();
    return ans;
}

public Integer[] gen2() {
    Integer[] ans = new Integer[3];
    ans[0] = new Integer(0);
    ans[1] = new Integer(0);
    ans[2] = new Integer(0);
    return ans;
}

public static void main(String[] args) {

    GenericArray<Integer> a = new GenericArray<Integer>() {

        @Override
        public Integer create() {
            return new Integer(0);
        }
    };

    Integer[] b = a.gen2();
    Integer[] c = a.gen1(); // this causes ClassCastException

    System.out.println("done");

}

}

为什么无法从balls可变参数中获取Class<T>,假设有一个? if(balls.length > 0) Class<T> clazz = balls[0].getClass(); 无论如何,你不可避免地会听到“数组和泛型不搭配”的说法。 - Matt Ball
最简单的方法是不使用数组,而是使用某种类型的“集合”(Collection)。 - Jeffrey
4个回答

6

1. 数组 不是 通用的

2. 这就是为什么 数组 在编译时和运行时都要检查,而 集合 可以是通用的,只在编译时检查....


那么,当只有一般信息时,创建一个类型的数组是不可能的吗? - Dims
你可以使用类似这样的代码 @SuppressWarnings("unchecked") B<Integer>[] arr = new B[3]; 但是你需要抑制编译器警告...但是为什么要这样做呢?因为在运行时没有泛型信息,而且我不喜欢在Java中使用数组。集合提供了更大的灵活性。 - Kumar Vivek Mitra
1
是的,或者更确切地说:您一开始就没有泛型信息,因为它在运行时被擦除(至少在纯Java中是这样)。这是一个在编译时失去完全的特性,一旦您的应用程序运行就会消失。 - sarcan
@sarcan,是这样的...这个过程被称为“擦除”,编译器会从类、方法等中删除类型参数和参数参数。使其成为原始类型(原始类型是没有类型参数的泛型类或接口名称)。 - Kumar Vivek Mitra

1

(T[][]) new Object[size][size]

(T[][]) new Object[size][size]


1
你所要求的是不可能的。一个数组在运行时知道它的组件类型,不同组件类型的数组是不同的运行时类型。这就是为什么当你创建数组时,需要在运行时知道组件类型。
由于你的方法是泛型的,调用者可以在每次调用该方法时向编译器指定它想要使用的T。因此,请考虑这是多么荒谬:
String[][] foo = this.<String>distribute();
Integer[][] bar = this.<Integer>distribute();

右侧的编译代码对于这两行是相同的。 .<String> 只是编译器的提示,不影响编译代码。 这意味着 distribute() 必须返回既是 String[][] 也是 Integer[][] 的内容(然后在运行时进行检查,因为 String[][]Integer[][] 是具体化的运行时类型)。唯一可以满足此要求的值是 null
(你可能会问,为什么返回 List<T> 的方法不会出现这个问题?答案是,与数组不同,运行时只有一个类 List。列表在运行时不知道其组件类型。因此,new ArrayList<Foo>()new ArrayList<Bar>() 在运行时是完全相同的。所以它们没有这个问题。)
另一个类比可以解释这里正在发生的事情:数组类型具有遵循其组件类型继承模式的继承模式。因此,Integer[][]Object[][] 的子类。实际上,所有的 T[][] 都是 Object[][] 的子类。因此,我们不妨考虑一个非数组类 MyBaseClass,它有一堆子类。那么,你基本上要求能够通用地创建一个未知的 MyBaseClass 子类实例(由类型参数确定)。
<T extends MyBaseClass> T distribute() {
    T answer = //...?
    return answer;
}

我希望您能明白这是不可能的。


0

做法:T[][] answer = (T[][]) new Object[][];
T 会被编译器擦除为 Object。当然,你需要知道数组的大小。

更新:
在你的新例子中,你会在这里得到异常:
Integer[] c = a.gen1(); // this causes ClassCastException
因为你试图将一个 Object[] 强制转换为一个 Integer[]。这是不可能的。
你得到的是一个 Object[],但其中包含 Integer 引用。所以你需要这样做:

Object[] c = a.gen1();
for(Object n:c){  
    Integer nn = (Integer) n;  
}  

这是没问题的,因为n是一个整数
但作为一般规则:如果您需要收集参数化类型对象,只需使用ArrayList。在您的示例中,甚至没有实际的通过反射来实例化数组,这是唯一明智的选择。


1
这不会在运行时引起“ClassCastException”吗? - Dims
不行,因为类型参数 T 将被编译器擦除,并且将被替换为 Object。这被称为 类型擦除。而你正在向 Object 数组中添加特定的对象,这是合法的。 - Cratylus
@Dims еЏҒжњ‰ењЁдҢ иҮ•е›ң将其分й…Қз»™Tзљ„е…·дҢ“з±»еһ‹ж—¶ж‰ҚдәљеҮәи‡өClassCastException: String[] foo = foo(); public static <T> T[] foo() { return (T[]) new Object[5]; } - Jeffrey
@Jeffrey @user384796,我已经尝试过了,但是它引发了“ClassCastException”。我需要创建普通类型的普通数组。 - Dims

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接