由于Java泛型的实现,你不能有这样的代码:

public class GenSet<E> {
    private E a[];

    public GenSet() {
        a = new E[INITIAL_ARRAY_LENGTH]; // error: generic array creation
    }
}

如何在保持类型安全的同时实现这一点?

我在Java论坛上看到一个解决方案是这样的:

import java.lang.reflect.Array;

class Stack<T> {
    public Stack(Class<T> clazz, int capacity) {
        array = (T[])Array.newInstance(clazz, capacity);
    }

    private final T[] array;
}

但我真的不明白这是怎么回事。


当前回答

要扩展到更多维度,只需将[]和维度参数添加到newInstance() (T是类型参数,cls是Class<T>, d1到d5是整数):

T[] array = (T[])Array.newInstance(cls, d1);
T[][] array = (T[][])Array.newInstance(cls, d1, d2);
T[][][] array = (T[][][])Array.newInstance(cls, d1, d2, d3);
T[][][][] array = (T[][][][])Array.newInstance(cls, d1, d2, d3, d4);
T[][][][][] array = (T[][][][][])Array.newInstance(cls, d1, d2, d3, d4, d5);

详情请参阅Array.newInstance()。

其他回答

您不需要将Class参数传递给构造函数。 试试这个。

public class GenSet<T> {

    private final T[] array;

    @SafeVarargs
    public GenSet(int capacity, T... dummy) {
        if (dummy.length > 0)
            throw new IllegalArgumentException(
              "Do not provide values for dummy argument.");
        this.array = Arrays.copyOf(dummy, capacity);
    }

    @Override
    public String toString() {
        return "GenSet of " + array.getClass().getComponentType().getName()
            + "[" + array.length + "]";
    }
}

and

GenSet<Integer> intSet = new GenSet<>(3);
System.out.println(intSet);
System.out.println(new GenSet<String>(2));

结果:

GenSet of java.lang.Integer[3]
GenSet of java.lang.String[2]

下面是如何使用泛型来获得你正在寻找的精确类型的数组,同时保持类型安全(与其他答案相反,后者要么会返回Object数组,要么会在编译时产生警告):

import java.lang.reflect.Array;  

public class GenSet<E> {  
    private E[] a;  

    public GenSet(Class<E[]> clazz, int length) {  
        a = clazz.cast(Array.newInstance(clazz.getComponentType(), length));  
    }  

    public static void main(String[] args) {  
        GenSet<String> foo = new GenSet<String>(String[].class, 1);  
        String[] bar = foo.a;  
        foo.a[0] = "xyzzy";  
        String baz = foo.a[0];  
    }  
}

编译时没有警告,正如你在main中看到的,无论你声明GenSet的实例为什么类型,你都可以将a赋值给该类型的数组,你也可以将a中的元素赋值给该类型的变量,这意味着数组和数组中的值都是正确的类型。

它通过使用类文字作为运行时类型标记来工作,如Java教程中讨论的那样。类字面量被编译器视为java.lang.Class的实例。要使用它,只需在类名后面加上.class。因此,String. Class充当了表示类String的Class对象。这也适用于接口、枚举、任何维度的数组(例如String[].class)、原语(例如int.class)和关键字void(例如void.class)。

Class本身是泛型的(声明为Class<T>,其中T代表Class对象所表示的类型),这意味着String. Class的类型是Class<String>。

因此,无论何时调用GenSet的构造函数,您都要传入一个类字面量作为表示GenSet实例声明类型的数组的第一个参数(例如String[].class for GenSet<String>)。请注意,您将无法获得一个原语数组,因为原语不能用于类型变量。

Inside the constructor, calling the method cast returns the passed Object argument cast to the class represented by the Class object on which the method was called. Calling the static method newInstance in java.lang.reflect.Array returns as an Object an array of the type represented by the Class object passed as the first argument and of the length specified by the int passed as the second argument. Calling the method getComponentType returns a Class object representing the component type of the array represented by the Class object on which the method was called (e.g. String.class for String[].class, null if the Class object doesn't represent an array).

最后一句不完全准确。调用String[].class.getComponentType()返回一个表示类String的Class对象,但它的类型是Class<?>,而不是Class<String>,这就是为什么你不能做下面这样的事情。

String foo = String[].class.getComponentType().cast("bar"); // won't compile

对于Class中返回Class对象的每个方法也是如此。

关于Joachim Sauer对这个答案的评论(我自己没有足够的声誉来评论它),使用转换为T[]的示例将导致一个警告,因为在这种情况下编译器不能保证类型安全。


编辑关于Ingo的评论:

public static <T> T[] newArray(Class<T[]> type, int size) {
   return type.cast(Array.newInstance(type.getComponentType(), size));
}

这是类型安全的唯一答案

E[] a;

a = newArray(size);

@SafeVarargs
static <E> E[] newArray(int length, E... array)
{
    return Arrays.copyOf(array, length);
}

我制作这个代码片段是为了反射地实例化一个简单的自动化测试实用程序所通过的类。

Object attributeValue = null;
try {
    if(clazz.isArray()){
        Class<?> arrayType = clazz.getComponentType();
        attributeValue = Array.newInstance(arrayType, 0);
    }
    else if(!clazz.isInterface()){
        attributeValue = BeanUtils.instantiateClass(clazz);
    }
} catch (Exception e) {
    logger.debug("Cannot instanciate \"{}\"", new Object[]{clazz});
}

注意这段:

    if(clazz.isArray()){
        Class<?> arrayType = clazz.getComponentType();
        attributeValue = Array.newInstance(arrayType, 0);
    }

用于数组初始化。newInstance(数组的类,数组的大小)。类可以是原语(int.class)和对象(Integer.class)。

BeanUtils是Spring的一部分。

传递一个值列表…

public <T> T[] array(T... values) {
    return values;
}