协慌网

登录 贡献 社区

如何在 Java 中创建泛型数组?

1
zhoujin7
贡献值 16
贡献次数 1

由于 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;
}

但我真的不知道发生了什么。

答案

1
zhoujin7
贡献值 59
贡献次数 1

下面是如何使用泛型来获得一个正在寻找的类型的数组,同时保留类型安全性(与其他答案相反,后者将返回一个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充当表示类StringClass对象。这也适用于接口,枚举,任何维数组(例如String[].class ),基元(例如int.class )和关键字void (即void.class )。

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

因此,每当您调用GenSet的构造函数时,都会为第一个参数传递一个类文字,表示GenSet实例的声明类型的数组(例如, String[].classGenSet<String> )。请注意,您将无法获取基元数组,因为基元不能用于类型变量。

在构造函数内部,调用方法cast将传递的Object参数强制转换为由调用该方法的Class对象表示的Class 。在java.lang.reflect.Array调用静态方法newInstance作为Object返回由作为第一个参数传递的Class对象表示的类型的数组,以及作为第二个参数传递的int指定的长度。调用该方法getComponentType返回一个Class表示组件类型由所表示的所述阵列的对象Class的对象在其上调用的方法(例如String.classString[].classnull ,如果Class对象不表示一个数组) 。

最后一句话并不完全准确。调用String[].class.getComponentType()返回一个表示类StringClass对象,但它的类型是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));
}