如何在Java中创建一个通用数组?

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

作为回报,我必须问一个问题:你的“检查”还是“未检查”?那是什么意思?GenSet

  • 已选中强类型。 显式知道它包含什么类型的对象(即它的构造函数是用参数显式调用的,并且当它们传递的参数不是类型时,方法将引发异常。请参阅 Collections.checkedCollectionGenSetClass<E>E

    ->在这种情况下,您应该写:

    public class GenSet<E> {
    
        private E[] a;
    
        public GenSet(Class<E> c, int s) {
            // Use Array native method to create array
            // of a type only known at run time
            @SuppressWarnings("unchecked")
            final E[] a = (E[]) Array.newInstance(c, s);
            this.a = a;
        }
    
        E get(int i) {
            return a[i];
        }
    }
    
  • 未选中弱类型。实际上不会对任何作为参数传递的对象执行类型检查。

    ->在这种情况下,你应该写

    public class GenSet<E> {
    
        private Object[] a;
    
        public GenSet(int s) {
            a = new Object[s];
        }
    
        E get(int i) {
            @SuppressWarnings("unchecked")
            final E e = (E) a[i];
            return e;
        }
    }
    

    请注意,数组的组件类型应该是类型参数的擦除

    public class GenSet<E extends Foo> { // E has an upper bound of Foo
    
        private Foo[] a; // E erases to Foo, so use Foo[]
    
        public GenSet(int s) {
            a = new Foo[s];
        }
    
        ...
    }
    

所有这些都源于Java中泛型的一个已知的,故意的弱点:它是使用擦除实现的,因此“泛型”类不知道它们在运行时是用什么类型参数创建的,因此除非实现一些显式机制(类型检查),否则无法提供类型安全。


答案 2

您可以执行以下操作:

E[] arr = (E[])new Object[INITIAL_ARRAY_LENGTH];

这是在 Effective Java 中实现泛型集合的建议方法之一。项目26.没有类型错误,无需重复转换数组。但是,这会触发警告,因为它具有潜在危险,应谨慎使用。如注释中所述,这现在伪装成我们的类型,如果不安全地使用,可能会导致意外的错误。Object[]E[]ClassCastException

根据经验,只要在内部使用强制转换数组(例如,支持数据结构),并且不返回或向客户端代码公开,此行为就是安全的。如果需要将泛型类型的数组返回到其他代码,则提到的反射类是正确的方法。Array


值得一提的是,只要有可能,如果你使用的是泛型,你会有一个更快乐的时间使用 s 而不是数组。当然,有时您别无选择,但使用集合框架要强大得多。List